@collabdt/core 0.0.42 → 0.0.43
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/Toolbar.d.ts.map +1 -1
- package/dist/core/components/Toolbar.js.map +1 -1
- package/dist/core/components/ToolbarBody.js.map +1 -1
- package/dist/core/components/TopNavigationBar.js.map +1 -1
- package/dist/core/components/authentication/ForgotPassword.js.map +1 -1
- package/dist/core/components/authentication/Signin.js.map +1 -1
- package/dist/core/components/authentication/Signup.js.map +1 -1
- package/dist/core/components/settings/src/OrganizationSkeleton.js.map +1 -1
- package/dist/core/components/settings/src/SettingsSkeleton.js.map +1 -1
- package/dist/core/components/ui/Comments/CollapsibleCommentItem.d.ts.map +1 -1
- package/dist/core/components/ui/Comments/CollapsibleCommentItem.js.map +1 -1
- package/dist/core/components/ui/Comments/CommentsSection.d.ts.map +1 -1
- package/dist/core/components/ui/Comments/CommentsSection.js.map +1 -1
- package/dist/core/components/ui/DataTable.d.ts.map +1 -1
- package/dist/core/components/ui/DataTable.js +7 -0
- package/dist/core/components/ui/DataTable.js.map +1 -1
- package/dist/core/components/ui/FilesManager/src/FileItemComponent.d.ts.map +1 -1
- package/dist/core/components/ui/FilesManager/src/FileItemComponent.js.map +1 -1
- package/dist/core/components/ui/FilesManager/src/FileMarker.d.ts +5 -2
- package/dist/core/components/ui/FilesManager/src/FileMarker.d.ts.map +1 -1
- package/dist/core/components/ui/FilesManager/src/FileMarker.js +84 -21
- package/dist/core/components/ui/FilesManager/src/FileMarker.js.map +1 -1
- package/dist/core/components/ui/FilesManager/src/convertIfcToFragmentsFile.d.ts.map +1 -1
- package/dist/core/components/ui/FilesManager/src/convertIfcToFragmentsFile.js.map +1 -1
- package/dist/core/components/ui/FilesManager/src/useFileUploadHandler.d.ts.map +1 -1
- package/dist/core/components/ui/FilesManager/src/useFileUploadHandler.js.map +1 -1
- package/dist/core/components/ui/Icons/IfcIcon.d.ts.map +1 -1
- package/dist/core/components/ui/Icons/IfcIcon.js.map +1 -1
- package/dist/core/components/ui/InfoSidebar/index.d.ts.map +1 -1
- package/dist/core/components/ui/InfoSidebar/index.js.map +1 -1
- package/dist/core/components/ui/Navbar.d.ts.map +1 -1
- package/dist/core/components/ui/Navbar.js.map +1 -1
- package/dist/core/components/ui/Sensors/CollapsibleSensorItem.d.ts.map +1 -1
- package/dist/core/components/ui/Sensors/CollapsibleSensorItem.js.map +1 -1
- package/dist/core/components/ui/Sensors/SensorTagsSection.d.ts.map +1 -1
- package/dist/core/components/ui/Sensors/SensorTagsSection.js.map +1 -1
- package/dist/core/components/ui/Sensors/SensorsSection.d.ts.map +1 -1
- package/dist/core/components/ui/Sensors/SensorsSection.js.map +1 -1
- package/dist/core/components/ui/ShareFeature/ShareToolSubmenu.d.ts.map +1 -1
- package/dist/core/components/ui/ShareFeature/ShareToolSubmenu.js.map +1 -1
- package/dist/core/components/viewers/Data/buildingDetails/GeocoderInput.js +2 -2
- package/dist/core/components/viewers/Data/buildingDetails/GeocoderInput.js.map +1 -1
- package/dist/core/components/viewers/Data/siteDetails/AssociatedBuildings.js +2 -2
- package/dist/core/components/viewers/Data/siteDetails/AssociatedBuildings.js.map +1 -1
- package/dist/core/components/viewers/Viewer.d.ts.map +1 -1
- package/dist/core/components/viewers/Viewer.js +5 -1
- package/dist/core/components/viewers/Viewer.js.map +1 -1
- package/dist/core/components/viewers/bim/BimToolbar.d.ts.map +1 -1
- package/dist/core/components/viewers/bim/BimToolbar.js.map +1 -1
- package/dist/core/components/viewers/bim/BimViewer.d.ts.map +1 -1
- package/dist/core/components/viewers/bim/BimViewer.js +14 -5
- package/dist/core/components/viewers/bim/BimViewer.js.map +1 -1
- package/dist/core/components/viewers/bim/src/BimLoadingState/index.d.ts.map +1 -1
- package/dist/core/components/viewers/bim/src/BimLoadingState/index.js +6 -0
- package/dist/core/components/viewers/bim/src/BimLoadingState/index.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 +168 -39
- 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.map +1 -1
- package/dist/core/components/viewers/bim/src/BimSidebar/src/LayersTab/src/FloorplanSection.d.ts.map +1 -1
- package/dist/core/components/viewers/bim/src/BimSidebar/src/LayersTab/src/FloorplanSection.js +1 -1
- package/dist/core/components/viewers/bim/src/BimSidebar/src/LayersTab/src/FloorplanSection.js.map +1 -1
- package/dist/core/components/viewers/bim/src/DXFLoader/index.d.ts +14 -37
- package/dist/core/components/viewers/bim/src/DXFLoader/index.d.ts.map +1 -1
- package/dist/core/components/viewers/bim/src/DXFLoader/index.js +31 -318
- package/dist/core/components/viewers/bim/src/DXFLoader/index.js.map +1 -1
- package/dist/core/components/viewers/bim/src/FloorplanTool/index.d.ts +66 -7
- package/dist/core/components/viewers/bim/src/FloorplanTool/index.d.ts.map +1 -1
- package/dist/core/components/viewers/bim/src/FloorplanTool/index.js +253 -21
- package/dist/core/components/viewers/bim/src/FloorplanTool/index.js.map +1 -1
- package/dist/core/components/viewers/bim/src/ModelManager/index.d.ts.map +1 -1
- package/dist/core/components/viewers/bim/src/ModelManager/index.js +9 -0
- package/dist/core/components/viewers/bim/src/ModelManager/index.js.map +1 -1
- package/dist/core/components/viewers/bim/src/SimpleBimViewer.d.ts.map +1 -1
- package/dist/core/components/viewers/bim/src/SimpleBimViewer.js.map +1 -1
- package/dist/core/components/viewers/bim/src/lib/TrueNorthPopover.d.ts +3 -2
- package/dist/core/components/viewers/bim/src/lib/TrueNorthPopover.d.ts.map +1 -1
- package/dist/core/components/viewers/bim/src/lib/TrueNorthPopover.js.map +1 -1
- package/dist/core/components/viewers/bim/src/lib/useFriendlyIfcClassName.d.ts.map +1 -1
- package/dist/core/components/viewers/bim/src/lib/useFriendlyIfcClassName.js +3 -4
- package/dist/core/components/viewers/bim/src/lib/useFriendlyIfcClassName.js.map +1 -1
- 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 +40 -18
- package/dist/core/components/viewers/bim/src/tools/AddToBim/index.js.map +1 -1
- package/dist/core/components/viewers/bim/src/tools/AddToBim/src/AddDxf.d.ts +2 -5
- package/dist/core/components/viewers/bim/src/tools/AddToBim/src/AddDxf.d.ts.map +1 -1
- package/dist/core/components/viewers/bim/src/tools/AddToBim/src/AddDxf.js +45 -59
- package/dist/core/components/viewers/bim/src/tools/AddToBim/src/AddDxf.js.map +1 -1
- package/dist/core/components/viewers/bim/src/tools/AddToBim/src/BimSensor.js +1 -0
- 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/FileHandler.d.ts +10 -1
- package/dist/core/components/viewers/bim/src/tools/AddToBim/src/FileHandler.d.ts.map +1 -1
- package/dist/core/components/viewers/bim/src/tools/AddToBim/src/FileHandler.js +25 -73
- package/dist/core/components/viewers/bim/src/tools/AddToBim/src/FileHandler.js.map +1 -1
- package/dist/core/components/viewers/bim/src/tools/AddToBim/src/FileMarkerUtils.d.ts +7 -3
- package/dist/core/components/viewers/bim/src/tools/AddToBim/src/FileMarkerUtils.d.ts.map +1 -1
- package/dist/core/components/viewers/bim/src/tools/AddToBim/src/FileMarkerUtils.js +42 -78
- package/dist/core/components/viewers/bim/src/tools/AddToBim/src/FileMarkerUtils.js.map +1 -1
- package/dist/core/components/viewers/bim/src/tools/AddToBim/src/Position3DCard.d.ts.map +1 -1
- package/dist/core/components/viewers/bim/src/tools/AddToBim/src/Position3DCard.js +52 -75
- package/dist/core/components/viewers/bim/src/tools/AddToBim/src/Position3DCard.js.map +1 -1
- package/dist/core/components/viewers/bim/src/tools/AddToBim/src/useFilePlacement.d.ts +15 -3
- package/dist/core/components/viewers/bim/src/tools/AddToBim/src/useFilePlacement.d.ts.map +1 -1
- package/dist/core/components/viewers/bim/src/tools/AddToBim/src/useFilePlacement.js +165 -105
- package/dist/core/components/viewers/bim/src/tools/AddToBim/src/useFilePlacement.js.map +1 -1
- package/dist/core/components/viewers/index.d.ts.map +1 -1
- package/dist/core/components/viewers/index.js.map +1 -1
- package/dist/core/components/viewers/map/MapViewer.d.ts.map +1 -1
- package/dist/core/components/viewers/map/MapViewer.js +1 -0
- package/dist/core/components/viewers/map/MapViewer.js.map +1 -1
- package/dist/core/components/viewers/map/datasets/DatasetManager/index.d.ts.map +1 -1
- package/dist/core/components/viewers/map/datasets/DatasetManager/index.js +4 -3
- package/dist/core/components/viewers/map/datasets/DatasetManager/index.js.map +1 -1
- package/dist/core/components/viewers/map/datasets/RowActions.d.ts.map +1 -1
- package/dist/core/components/viewers/map/datasets/RowActions.js +148 -0
- package/dist/core/components/viewers/map/datasets/RowActions.js.map +1 -1
- package/dist/core/components/viewers/map/datasets/index.d.ts.map +1 -1
- package/dist/core/components/viewers/map/datasets/index.js +32 -15
- package/dist/core/components/viewers/map/datasets/index.js.map +1 -1
- package/dist/core/components/viewers/map/datasets/src/builtinLiveDatasets.d.ts +4 -0
- package/dist/core/components/viewers/map/datasets/src/builtinLiveDatasets.d.ts.map +1 -0
- package/dist/core/components/viewers/map/datasets/src/builtinLiveDatasets.js +43 -0
- package/dist/core/components/viewers/map/datasets/src/builtinLiveDatasets.js.map +1 -0
- package/dist/core/components/viewers/map/datasets/src/localDatasets.d.ts.map +1 -1
- package/dist/core/components/viewers/map/datasets/src/localDatasets.js +39 -3
- package/dist/core/components/viewers/map/datasets/src/localDatasets.js.map +1 -1
- package/dist/core/components/viewers/map/datasets/src/minioDatasets.d.ts +1 -1
- package/dist/core/components/viewers/map/datasets/src/minioDatasets.d.ts.map +1 -1
- package/dist/core/components/viewers/map/datasets/src/minioDatasets.js +2 -1
- package/dist/core/components/viewers/map/datasets/src/minioDatasets.js.map +1 -1
- package/dist/core/components/viewers/map/datasets/src/publishedTiles.d.ts +47 -0
- package/dist/core/components/viewers/map/datasets/src/publishedTiles.d.ts.map +1 -0
- package/dist/core/components/viewers/map/datasets/src/publishedTiles.js +114 -0
- package/dist/core/components/viewers/map/datasets/src/publishedTiles.js.map +1 -0
- package/dist/core/components/viewers/map/datasets/src/wmsTime.d.ts +49 -0
- package/dist/core/components/viewers/map/datasets/src/wmsTime.d.ts.map +1 -0
- package/dist/core/components/viewers/map/datasets/src/wmsTime.js +100 -0
- package/dist/core/components/viewers/map/datasets/src/wmsTime.js.map +1 -0
- package/dist/core/components/viewers/map/src/Geocoder.d.ts.map +1 -1
- package/dist/core/components/viewers/map/src/Geocoder.js +9 -9
- package/dist/core/components/viewers/map/src/Geocoder.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 +25 -43
- package/dist/core/components/viewers/map/src/MapFeaturePopoverMenu/src/NonDatabaseBuildingPopover.js.map +1 -1
- package/dist/core/components/viewers/map/src/MapLayers/index.d.ts.map +1 -1
- package/dist/core/components/viewers/map/src/MapLayers/index.js.map +1 -1
- package/dist/core/components/viewers/map/src/MapLayers/src/BimLayer/index.d.ts.map +1 -1
- package/dist/core/components/viewers/map/src/MapLayers/src/BimLayer/index.js +19 -2
- package/dist/core/components/viewers/map/src/MapLayers/src/BimLayer/index.js.map +1 -1
- package/dist/core/components/viewers/map/src/MapLayers/src/CountryLayer/countryLayerUtils.d.ts +9 -0
- package/dist/core/components/viewers/map/src/MapLayers/src/CountryLayer/countryLayerUtils.d.ts.map +1 -0
- package/dist/core/components/viewers/map/src/MapLayers/src/CountryLayer/countryLayerUtils.js +28 -0
- package/dist/core/components/viewers/map/src/MapLayers/src/CountryLayer/countryLayerUtils.js.map +1 -0
- package/dist/core/components/viewers/map/src/MapLayers/src/CountryLayer/index.d.ts.map +1 -1
- package/dist/core/components/viewers/map/src/MapLayers/src/CountryLayer/index.js +1 -16
- package/dist/core/components/viewers/map/src/MapLayers/src/CountryLayer/index.js.map +1 -1
- package/dist/core/components/viewers/map/src/MapLayers/src/FileLayer/FileModelLayer/FileModelLayer.d.ts.map +1 -1
- package/dist/core/components/viewers/map/src/MapLayers/src/FileLayer/FileModelLayer/FileModelLayer.js.map +1 -1
- package/dist/core/components/viewers/map/src/MapLayers/src/FileLayer/utils/CustomModelLayer.d.ts.map +1 -1
- package/dist/core/components/viewers/map/src/MapLayers/src/FileLayer/utils/CustomModelLayer.js +31 -2
- package/dist/core/components/viewers/map/src/MapLayers/src/FileLayer/utils/CustomModelLayer.js.map +1 -1
- package/dist/core/components/viewers/map/src/MapLayers/src/OpenDataLayer/src/WmsTimeControl.d.ts +21 -0
- package/dist/core/components/viewers/map/src/MapLayers/src/OpenDataLayer/src/WmsTimeControl.d.ts.map +1 -0
- package/dist/core/components/viewers/map/src/MapLayers/src/OpenDataLayer/src/WmsTimeControl.js +111 -0
- package/dist/core/components/viewers/map/src/MapLayers/src/OpenDataLayer/src/WmsTimeControl.js.map +1 -0
- package/dist/core/components/viewers/map/src/MapLayers/src/OpenDataLayer/src/index.d.ts.map +1 -1
- package/dist/core/components/viewers/map/src/MapLayers/src/OpenDataLayer/src/index.js +77 -2
- package/dist/core/components/viewers/map/src/MapLayers/src/OpenDataLayer/src/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.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.map +1 -1
- package/dist/core/components/viewers/map/src/MapSidebar/src/SettingsTab/src/TerrainLevel.d.ts.map +1 -1
- package/dist/core/components/viewers/map/src/MapSidebar/src/SettingsTab/src/TerrainLevel.js +10 -3
- package/dist/core/components/viewers/map/src/MapSidebar/src/SettingsTab/src/TerrainLevel.js.map +1 -1
- package/dist/core/components/viewers/map/utils/geocoder.d.ts +6 -2
- package/dist/core/components/viewers/map/utils/geocoder.d.ts.map +1 -1
- package/dist/core/components/viewers/map/utils/geocoder.js +10 -62
- package/dist/core/components/viewers/map/utils/geocoder.js.map +1 -1
- package/dist/core/components/viewers/map/utils/geocoding/adapters.d.ts +5 -0
- package/dist/core/components/viewers/map/utils/geocoding/adapters.d.ts.map +1 -0
- package/dist/core/components/viewers/map/utils/geocoding/adapters.js +192 -0
- package/dist/core/components/viewers/map/utils/geocoding/adapters.js.map +1 -0
- package/dist/core/components/viewers/map/utils/geocoding/config.d.ts +6 -0
- package/dist/core/components/viewers/map/utils/geocoding/config.d.ts.map +1 -0
- package/dist/core/components/viewers/map/utils/geocoding/config.js +14 -0
- package/dist/core/components/viewers/map/utils/geocoding/config.js.map +1 -0
- package/dist/core/components/viewers/map/utils/geocoding/index.d.ts +8 -0
- package/dist/core/components/viewers/map/utils/geocoding/index.d.ts.map +1 -0
- package/dist/core/components/viewers/map/utils/geocoding/index.js +44 -0
- package/dist/core/components/viewers/map/utils/geocoding/index.js.map +1 -0
- package/dist/core/components/viewers/map/utils/geocoding/osm.d.ts +6 -0
- package/dist/core/components/viewers/map/utils/geocoding/osm.d.ts.map +1 -0
- package/dist/core/components/viewers/map/utils/geocoding/osm.js +39 -0
- package/dist/core/components/viewers/map/utils/geocoding/osm.js.map +1 -0
- package/dist/core/components/viewers/map/utils/geocoding/pelias.d.ts +8 -0
- package/dist/core/components/viewers/map/utils/geocoding/pelias.d.ts.map +1 -0
- package/dist/core/components/viewers/map/utils/geocoding/pelias.js +35 -0
- package/dist/core/components/viewers/map/utils/geocoding/pelias.js.map +1 -0
- package/dist/core/components/viewers/pointcloud/PointCloudToolbar.d.ts.map +1 -1
- package/dist/core/components/viewers/pointcloud/PointCloudToolbar.js.map +1 -1
- package/dist/core/components/viewers/pointcloud/PointCloudViewer.js +1 -3
- package/dist/core/components/viewers/pointcloud/PointCloudViewer.js.map +1 -1
- package/dist/core/components/viewers/pointcloud/src/PointCloudSidebar/src/FileTab/index.d.ts.map +1 -1
- package/dist/core/components/viewers/pointcloud/src/PointCloudSidebar/src/FileTab/index.js +2 -3
- package/dist/core/components/viewers/pointcloud/src/PointCloudSidebar/src/FileTab/index.js.map +1 -1
- package/dist/core/components/viewers/pointcloud/src/PointCloudSidebar/src/FileTab/src/FilesSection.d.ts.map +1 -1
- package/dist/core/components/viewers/pointcloud/src/PointCloudSidebar/src/FileTab/src/FilesSection.js +8 -2
- package/dist/core/components/viewers/pointcloud/src/PointCloudSidebar/src/FileTab/src/FilesSection.js.map +1 -1
- package/dist/core/components/viewers/pointcloud/src/PointCloudSidebar/src/FileTab/src/PointCloudSection.d.ts.map +1 -1
- package/dist/core/components/viewers/pointcloud/src/PointCloudSidebar/src/FileTab/src/PointCloudSection.js.map +1 -1
- package/dist/core/components/viewers/pointcloud/src/tools/PerformanceSettingsTools/NodeSizeSelectionTool.js +9 -9
- package/dist/core/components/viewers/pointcloud/src/tools/PerformanceSettingsTools/NodeSizeSelectionTool.js.map +1 -1
- package/dist/core/hooks/provider.js.map +1 -1
- package/dist/core/plugins/host/provider.d.ts.map +1 -1
- package/dist/core/plugins/host/provider.js.map +1 -1
- package/dist/core/store/AppConfig/context.d.ts.map +1 -1
- package/dist/core/store/AppConfig/context.js.map +1 -1
- package/dist/core/store/BIM/context.d.ts.map +1 -1
- package/dist/core/store/BIM/context.js.map +1 -1
- package/dist/core/store/BIM/reducer.d.ts +0 -1
- package/dist/core/store/BIM/reducer.d.ts.map +1 -1
- package/dist/core/store/BIM/reducer.js +0 -4
- package/dist/core/store/BIM/reducer.js.map +1 -1
- package/dist/core/store/Buildings/context.d.ts.map +1 -1
- package/dist/core/store/Buildings/context.js.map +1 -1
- package/dist/core/store/Content/context.d.ts.map +1 -1
- package/dist/core/store/Content/context.js.map +1 -1
- package/dist/core/store/Datasets/context.d.ts.map +1 -1
- package/dist/core/store/Datasets/context.js +2 -1
- package/dist/core/store/Datasets/context.js.map +1 -1
- package/dist/core/store/Datasets/reducer.d.ts +2 -0
- package/dist/core/store/Datasets/reducer.d.ts.map +1 -1
- package/dist/core/store/Datasets/reducer.js +9 -4
- package/dist/core/store/Datasets/reducer.js.map +1 -1
- package/dist/core/store/Files/context.d.ts.map +1 -1
- package/dist/core/store/Files/context.js.map +1 -1
- package/dist/core/store/Map/context.d.ts.map +1 -1
- package/dist/core/store/Map/context.js.map +1 -1
- package/dist/core/store/Menus/context.d.ts.map +1 -1
- package/dist/core/store/Menus/context.js.map +1 -1
- package/dist/core/store/Permissions/context.d.ts.map +1 -1
- package/dist/core/store/Permissions/context.js.map +1 -1
- package/dist/core/store/PointCloud/context.d.ts.map +1 -1
- package/dist/core/store/PointCloud/context.js.map +1 -1
- package/dist/core/store/Tools/context.d.ts.map +1 -1
- package/dist/core/store/Tools/context.js.map +1 -1
- package/dist/core/types/datasetTypes.d.ts +8 -1
- package/dist/core/types/datasetTypes.d.ts.map +1 -1
- package/dist/core/types/dbTypes.d.ts +13 -13
- package/dist/core/types/dbTypes.d.ts.map +1 -1
- package/dist/core/types/dbTypes.js +13 -13
- package/dist/core/types/dbTypes.js.map +1 -1
- package/dist/core/types/index.d.ts +1 -1
- package/dist/core/types/index.d.ts.map +1 -1
- package/dist/core/types/index.js.map +1 -1
- package/dist/core/utils/imageUtils.js +1 -1
- package/dist/core/utils/imageUtils.js.map +1 -1
- package/package.json +4 -3
- package/dist/core/components/viewers/pointcloud/src/PointCloudManagement/PointCloudViewer-old.d.ts +0 -3
- package/dist/core/components/viewers/pointcloud/src/PointCloudManagement/PointCloudViewer-old.d.ts.map +0 -1
- package/dist/core/components/viewers/pointcloud/src/PointCloudManagement/PointCloudViewer-old.js +0 -173
- package/dist/core/components/viewers/pointcloud/src/PointCloudManagement/PointCloudViewer-old.js.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../src/core/components/ui/Sensors/CollapsibleSensorItem.tsx"],"sourcesContent":["'use client'\r\n\r\nimport * as React from 'react'\r\n// Audit Phase 1.F (F-2c): direct file imports — bypass ui barrel.\r\nimport { Button } from '../Button'\r\nimport { Badge } from '../Badge'\r\nimport { Avatar, AvatarFallback } from '../Avatar'\r\nimport { UserAvatar } from '../UserAvatar'\r\nimport * as LR from 'lucide-react'\r\nimport { useTranslations } from 'next-intl'\r\nimport { format } from 'date-fns'\r\nimport { cn } from '../../../utils/utils'\r\nimport { Sensor, SensorType } from '../../../types/dbTypes'\r\nimport { useUser } from '../../../hooks/users/users'\r\nimport { useCoreHooks } from '../../../hooks/provider'\r\nimport { useSession } from 'next-auth/react'\r\nimport { SensorChart } from './SensorChart'\r\nimport { SensorTagsSection } from './SensorTagsSection'\r\nimport type { ChartConfig } from '../chart'\r\n\r\ntype SensorAction = 'view' | 'edit' | 'delete' | 'reply'\r\n\r\ninterface CollapsibleSensorItemProps {\r\n sensor: Sensor\r\n sensorType: SensorType\r\n onAction?: (action: SensorAction, id: number) => void\r\n depth?: number\r\n isVisible?: boolean\r\n onMouseEnter?: () => void\r\n onMouseLeave?: () => void\r\n}\r\n\r\nexport function CollapsibleSensorItem({\r\n sensor,\r\n sensorType,\r\n onAction,\r\n depth = 0,\r\n isVisible = true,\r\n onMouseEnter,\r\n onMouseLeave\r\n}: CollapsibleSensorItemProps) {\r\n const t = useTranslations('SensorsSection')\r\n const [isExpanded, setIsExpanded] = React.useState(false)\r\n const [sensorData, setSensorData] = React.useState<{ time: string; value: number }[]>([])\r\n const [isLoadingData, setIsLoadingData] = React.useState(false)\r\n const prevVisibleRef = React.useRef(isVisible)\r\n\r\n const dataPath = `${process.env.NEXT_PUBLIC_MINIO_BUCKET_URL}/sensors/${sensor.url}`\r\n\r\n const typeIcon = sensorType?.icon || 'Radio'\r\n const typeName = sensorType?.name.replace(/_/g, ' ') ?? 'Unknown' \r\n\r\n const SensorIcon = LR[typeIcon] || LR.Radio\r\n\r\n // Auto-collapse when visibility turns off\r\n React.useEffect(() => {\r\n const prevVisible = prevVisibleRef.current\r\n \r\n if (prevVisible && !isVisible) {\r\n // Just turned invisible - collapse\r\n setIsExpanded(false)\r\n }\r\n \r\n prevVisibleRef.current = isVisible\r\n }, [isVisible])\r\n\r\n // Fetch and parse CSV data\r\n React.useEffect(() => {\r\n const fetchCSVData = async () => {\r\n setIsLoadingData(true)\r\n try {\r\n const response = await fetch(dataPath)\r\n const csvText = await response.text()\r\n \r\n // Parse CSV: format is \"time,value\" (e.g., \"0:00:00,7.4\")\r\n const lines = csvText.trim().split('\\n')\r\n const parsed = lines.map(line => {\r\n const [time, valueStr] = line.split(',')\r\n return {\r\n time: time.trim(),\r\n value: parseFloat(valueStr.trim())\r\n }\r\n }).filter(item => !isNaN(item.value)) // Filter out any invalid entries\r\n \r\n setSensorData(parsed)\r\n } catch (error) {\r\n console.error('Error fetching sensor data:', error)\r\n // Fallback to empty array on error\r\n setSensorData([])\r\n } finally {\r\n setIsLoadingData(false)\r\n }\r\n }\r\n\r\n if (isExpanded) {\r\n fetchCSVData()\r\n }\r\n }, [dataPath, isExpanded])\r\n\r\n const chartConfig = {\r\n value: {\r\n label: typeName,\r\n color: \"hsl(var(--chart-1))\",\r\n },\r\n } satisfies ChartConfig\r\n\r\n const user = useSession().data?.user\r\n\r\n const indentClass = depth > 0 ? `ml-${Math.min(depth * 4, 12)}` : ''\r\n\r\n const { user: author } = useUser(String(sensor.authorId))\r\n let authorName: string | undefined = author?.name ?? 'Unknown User'\r\n\r\n const { sensor: sensorHooks } = useCoreHooks()\r\n const { updateSensor } = sensorHooks.useSensor(sensor.id)\r\n\r\n const handleAddTag = async (tag: string) => {\r\n const currentTags = sensor.tags ?? []\r\n await updateSensor({ tags: [...currentTags, tag] })\r\n }\r\n\r\n const handleDeleteTag = async (tag: string) => {\r\n const currentTags = sensor.tags ?? []\r\n await updateSensor({ tags: currentTags.filter(t => t !== tag) })\r\n }\r\n\r\n return (\r\n <div \r\n className={cn(\r\n \"border rounded-md overflow-hidden transition-opacity\",\r\n indentClass,\r\n !isVisible && \"opacity-70\"\r\n )}\r\n onMouseEnter={onMouseEnter}\r\n onMouseLeave={onMouseLeave}\r\n >\r\n {/* Sensor Header */}\r\n <div className=\"flex items-start justify-between p-3 hover:bg-accent/50 transition-colors\">\r\n <div className=\"flex items-start gap-3 flex-1 min-w-0\">\r\n <Button\r\n variant=\"ghost\"\r\n size=\"sm\"\r\n className={cn(\"h-6 w-6 p-0 mt-1\", !isVisible && \"opacity-70\")}\r\n onClick={() => setIsExpanded(!isExpanded)}\r\n >\r\n <LR.ChevronRight\r\n className={cn(\r\n \"h-4 w-4 transition-transform\",\r\n isExpanded && \"rotate-90\"\r\n )}\r\n />\r\n </Button>\r\n\r\n <div className={cn(\r\n \"h-8 w-8 mt-1 rounded-full bg-primary flex items-center justify-center flex-shrink-0\",\r\n !isVisible && \"opacity-70 grayscale\"\r\n )}>\r\n <SensorIcon className={cn(\"h-4 w-4 text-primary-foreground\", !isVisible && \"opacity-70\")} />\r\n </div>\r\n\r\n <div className=\"flex-1 min-w-0\">\r\n <div className=\"flex items-center gap-2\">\r\n <span className={cn(\"text-sm font-medium\", isVisible ? \"text-foreground\" : \"text-muted-foreground\")}>\r\n {sensor.name}\r\n </span>\r\n {sensor.updatedAt !== sensor.createdAt && (\r\n <Badge variant=\"outline\" className=\"text-xs\">\r\n {t('edited')}\r\n </Badge>\r\n )}\r\n </div>\r\n </div>\r\n </div>\r\n\r\n {/* Action Buttons */}\r\n {onAction && (\r\n <div className={cn(\"flex items-center gap-0 flex-shrink-0\", !isVisible && \"opacity-70\")}>\r\n {user?.id === String(sensor.authorId) && (\r\n <>\r\n <Button\r\n variant=\"ghost\"\r\n size=\"icon\"\r\n className=\"h-8 w-8 p-0\"\r\n onClick={() => onAction('edit', sensor.id)}\r\n title={t('editSensor')}\r\n >\r\n <LR.Pencil className=\"h-4 w-4\" />\r\n </Button>\r\n <Button\r\n variant=\"ghost\"\r\n size=\"icon\"\r\n className=\"h-8 w-8 p-0\"\r\n onClick={() => onAction('delete', sensor.id)}\r\n title={t('deleteSensor')}\r\n >\r\n <LR.Trash2 className=\"h-4 w-4\" />\r\n </Button>\r\n </>\r\n )}\r\n </div>\r\n )}\r\n </div>\r\n\r\n {/* Expanded Content */ }\r\n {\r\n isExpanded && (\r\n <div className=\"border-t bg-muted/30\">\r\n <div className=\"p-4 space-y-4\">\r\n {/* Sensor Information */}\r\n <div className=\"space-y-2\">\r\n <div className=\"flex items-center justify-between\">\r\n <span className=\"text-xs text-muted-foreground\">\r\n {format(new Date(sensor.createdAt), 'PPp')}\r\n </span>\r\n <Badge variant=\"secondary\" className=\"text-xs\">\r\n {typeName}\r\n </Badge>\r\n </div>\r\n \r\n <div className=\"grid grid-cols-2 gap-2 text-sm\">\r\n <div>\r\n <span className=\"text-muted-foreground\">Name:</span>\r\n <span className=\"ml-2 font-medium\">{sensor.name}</span>\r\n </div>\r\n <div>\r\n <span className=\"text-muted-foreground\">Format:</span>\r\n <span className=\"ml-2 font-medium\">{sensor.dataFormat}</span>\r\n </div>\r\n </div>\r\n\r\n {/* Author Information */}\r\n <div className=\"flex items-center gap-2 pt-2\">\r\n <span className=\"text-muted-foreground text-sm\">Author:</span>\r\n <Avatar className=\"h-5 w-5 rounded-full overflow-hidden\">\r\n <UserAvatar imageFileId={author?.imageFileId} name={authorName} />\r\n </Avatar>\r\n <span className=\"text-sm font-medium\">{authorName}</span>\r\n </div>\r\n </div>\r\n\r\n {/* Sensor Data Chart */}\r\n <SensorChart\r\n sensorData={sensorData}\r\n isLoadingData={isLoadingData}\r\n sensorName={sensor.name}\r\n sensorType={sensorType}\r\n updateFrequency={sensor.updateFrequency}\r\n chartConfig={chartConfig}\r\n />\r\n </div>\r\n <div>\r\n <SensorTagsSection\r\n tags={sensor.tags ?? []}\r\n onAdd={handleAddTag}\r\n onDelete={handleDeleteTag}\r\n translations={{\r\n addTag: t('addTag'),\r\n removeTag: t('removeTag'),\r\n cancel: t('cancel'),\r\n newTagPlaceholder: t('newTagPlaceholder'),\r\n }}\r\n />\r\n </div>\r\n\r\n </div>\r\n )\r\n }\r\n </div >\r\n )\r\n}\r\n"],"mappings":"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","names":["_a","t"]}
|
|
1
|
+
{"version":3,"sources":["../../../../../src/core/components/ui/Sensors/CollapsibleSensorItem.tsx"],"sourcesContent":["'use client'\r\n\r\nimport * as React from 'react'\r\nimport { Button } from '../Button'\r\nimport { Badge } from '../Badge'\r\nimport { Avatar, AvatarFallback } from '../Avatar'\r\nimport { UserAvatar } from '../UserAvatar'\r\nimport * as LR from 'lucide-react'\r\nimport { useTranslations } from 'next-intl'\r\nimport { format } from 'date-fns'\r\nimport { cn } from '../../../utils/utils'\r\nimport { Sensor, SensorType } from '../../../types/dbTypes'\r\nimport { useUser } from '../../../hooks/users/users'\r\nimport { useCoreHooks } from '../../../hooks/provider'\r\nimport { useSession } from 'next-auth/react'\r\nimport { SensorChart } from './SensorChart'\r\nimport { SensorTagsSection } from './SensorTagsSection'\r\nimport type { ChartConfig } from '../chart'\r\n\r\ntype SensorAction = 'view' | 'edit' | 'delete' | 'reply'\r\n\r\ninterface CollapsibleSensorItemProps {\r\n sensor: Sensor\r\n sensorType: SensorType\r\n onAction?: (action: SensorAction, id: number) => void\r\n depth?: number\r\n isVisible?: boolean\r\n onMouseEnter?: () => void\r\n onMouseLeave?: () => void\r\n}\r\n\r\nexport function CollapsibleSensorItem({\r\n sensor,\r\n sensorType,\r\n onAction,\r\n depth = 0,\r\n isVisible = true,\r\n onMouseEnter,\r\n onMouseLeave\r\n}: CollapsibleSensorItemProps) {\r\n const t = useTranslations('SensorsSection')\r\n const [isExpanded, setIsExpanded] = React.useState(false)\r\n const [sensorData, setSensorData] = React.useState<{ time: string; value: number }[]>([])\r\n const [isLoadingData, setIsLoadingData] = React.useState(false)\r\n const prevVisibleRef = React.useRef(isVisible)\r\n\r\n const dataPath = `${process.env.NEXT_PUBLIC_MINIO_BUCKET_URL}/sensors/${sensor.url}`\r\n\r\n const typeIcon = sensorType?.icon || 'Radio'\r\n const typeName = sensorType?.name.replace(/_/g, ' ') ?? 'Unknown' \r\n\r\n const SensorIcon = LR[typeIcon] || LR.Radio\r\n\r\n // Auto-collapse when visibility turns off\r\n React.useEffect(() => {\r\n const prevVisible = prevVisibleRef.current\r\n \r\n if (prevVisible && !isVisible) {\r\n // Just turned invisible - collapse\r\n setIsExpanded(false)\r\n }\r\n \r\n prevVisibleRef.current = isVisible\r\n }, [isVisible])\r\n\r\n // Fetch and parse CSV data\r\n React.useEffect(() => {\r\n const fetchCSVData = async () => {\r\n setIsLoadingData(true)\r\n try {\r\n const response = await fetch(dataPath)\r\n const csvText = await response.text()\r\n \r\n // Parse CSV: format is \"time,value\" (e.g., \"0:00:00,7.4\")\r\n const lines = csvText.trim().split('\\n')\r\n const parsed = lines.map(line => {\r\n const [time, valueStr] = line.split(',')\r\n return {\r\n time: time.trim(),\r\n value: parseFloat(valueStr.trim())\r\n }\r\n }).filter(item => !isNaN(item.value)) // Filter out any invalid entries\r\n \r\n setSensorData(parsed)\r\n } catch (error) {\r\n console.error('Error fetching sensor data:', error)\r\n // Fallback to empty array on error\r\n setSensorData([])\r\n } finally {\r\n setIsLoadingData(false)\r\n }\r\n }\r\n\r\n if (isExpanded) {\r\n fetchCSVData()\r\n }\r\n }, [dataPath, isExpanded])\r\n\r\n const chartConfig = {\r\n value: {\r\n label: typeName,\r\n color: \"hsl(var(--chart-1))\",\r\n },\r\n } satisfies ChartConfig\r\n\r\n const user = useSession().data?.user\r\n\r\n const indentClass = depth > 0 ? `ml-${Math.min(depth * 4, 12)}` : ''\r\n\r\n const { user: author } = useUser(String(sensor.authorId))\r\n let authorName: string | undefined = author?.name ?? 'Unknown User'\r\n\r\n const { sensor: sensorHooks } = useCoreHooks()\r\n const { updateSensor } = sensorHooks.useSensor(sensor.id)\r\n\r\n const handleAddTag = async (tag: string) => {\r\n const currentTags = sensor.tags ?? []\r\n await updateSensor({ tags: [...currentTags, tag] })\r\n }\r\n\r\n const handleDeleteTag = async (tag: string) => {\r\n const currentTags = sensor.tags ?? []\r\n await updateSensor({ tags: currentTags.filter(t => t !== tag) })\r\n }\r\n\r\n return (\r\n <div \r\n className={cn(\r\n \"border rounded-md overflow-hidden transition-opacity\",\r\n indentClass,\r\n !isVisible && \"opacity-70\"\r\n )}\r\n onMouseEnter={onMouseEnter}\r\n onMouseLeave={onMouseLeave}\r\n >\r\n {/* Sensor Header */}\r\n <div className=\"flex items-start justify-between p-3 hover:bg-accent/50 transition-colors\">\r\n <div className=\"flex items-start gap-3 flex-1 min-w-0\">\r\n <Button\r\n variant=\"ghost\"\r\n size=\"sm\"\r\n className={cn(\"h-6 w-6 p-0 mt-1\", !isVisible && \"opacity-70\")}\r\n onClick={() => setIsExpanded(!isExpanded)}\r\n >\r\n <LR.ChevronRight\r\n className={cn(\r\n \"h-4 w-4 transition-transform\",\r\n isExpanded && \"rotate-90\"\r\n )}\r\n />\r\n </Button>\r\n\r\n <div className={cn(\r\n \"h-8 w-8 mt-1 rounded-full bg-primary flex items-center justify-center flex-shrink-0\",\r\n !isVisible && \"opacity-70 grayscale\"\r\n )}>\r\n <SensorIcon className={cn(\"h-4 w-4 text-primary-foreground\", !isVisible && \"opacity-70\")} />\r\n </div>\r\n\r\n <div className=\"flex-1 min-w-0\">\r\n <div className=\"flex items-center gap-2\">\r\n <span className={cn(\"text-sm font-medium\", isVisible ? \"text-foreground\" : \"text-muted-foreground\")}>\r\n {sensor.name}\r\n </span>\r\n {sensor.updatedAt !== sensor.createdAt && (\r\n <Badge variant=\"outline\" className=\"text-xs\">\r\n {t('edited')}\r\n </Badge>\r\n )}\r\n </div>\r\n </div>\r\n </div>\r\n\r\n {/* Action Buttons */}\r\n {onAction && (\r\n <div className={cn(\"flex items-center gap-0 flex-shrink-0\", !isVisible && \"opacity-70\")}>\r\n {user?.id === String(sensor.authorId) && (\r\n <>\r\n <Button\r\n variant=\"ghost\"\r\n size=\"icon\"\r\n className=\"h-8 w-8 p-0\"\r\n onClick={() => onAction('edit', sensor.id)}\r\n title={t('editSensor')}\r\n >\r\n <LR.Pencil className=\"h-4 w-4\" />\r\n </Button>\r\n <Button\r\n variant=\"ghost\"\r\n size=\"icon\"\r\n className=\"h-8 w-8 p-0\"\r\n onClick={() => onAction('delete', sensor.id)}\r\n title={t('deleteSensor')}\r\n >\r\n <LR.Trash2 className=\"h-4 w-4\" />\r\n </Button>\r\n </>\r\n )}\r\n </div>\r\n )}\r\n </div>\r\n\r\n {/* Expanded Content */ }\r\n {\r\n isExpanded && (\r\n <div className=\"border-t bg-muted/30\">\r\n <div className=\"p-4 space-y-4\">\r\n {/* Sensor Information */}\r\n <div className=\"space-y-2\">\r\n <div className=\"flex items-center justify-between\">\r\n <span className=\"text-xs text-muted-foreground\">\r\n {format(new Date(sensor.createdAt), 'PPp')}\r\n </span>\r\n <Badge variant=\"secondary\" className=\"text-xs\">\r\n {typeName}\r\n </Badge>\r\n </div>\r\n \r\n <div className=\"grid grid-cols-2 gap-2 text-sm\">\r\n <div>\r\n <span className=\"text-muted-foreground\">Name:</span>\r\n <span className=\"ml-2 font-medium\">{sensor.name}</span>\r\n </div>\r\n <div>\r\n <span className=\"text-muted-foreground\">Format:</span>\r\n <span className=\"ml-2 font-medium\">{sensor.dataFormat}</span>\r\n </div>\r\n </div>\r\n\r\n {/* Author Information */}\r\n <div className=\"flex items-center gap-2 pt-2\">\r\n <span className=\"text-muted-foreground text-sm\">Author:</span>\r\n <Avatar className=\"h-5 w-5 rounded-full overflow-hidden\">\r\n <UserAvatar imageFileId={author?.imageFileId} name={authorName} />\r\n </Avatar>\r\n <span className=\"text-sm font-medium\">{authorName}</span>\r\n </div>\r\n </div>\r\n\r\n {/* Sensor Data Chart */}\r\n <SensorChart\r\n sensorData={sensorData}\r\n isLoadingData={isLoadingData}\r\n sensorName={sensor.name}\r\n sensorType={sensorType}\r\n updateFrequency={sensor.updateFrequency}\r\n chartConfig={chartConfig}\r\n />\r\n </div>\r\n <div>\r\n <SensorTagsSection\r\n tags={sensor.tags ?? []}\r\n onAdd={handleAddTag}\r\n onDelete={handleDeleteTag}\r\n translations={{\r\n addTag: t('addTag'),\r\n removeTag: t('removeTag'),\r\n cancel: t('cancel'),\r\n newTagPlaceholder: t('newTagPlaceholder'),\r\n }}\r\n />\r\n </div>\r\n\r\n </div>\r\n )\r\n }\r\n </div >\r\n )\r\n}\r\n"],"mappings":"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","names":["_a","t"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SensorTagsSection.d.ts","sourceRoot":"","sources":["../../../../../src/core/components/ui/Sensors/SensorTagsSection.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;
|
|
1
|
+
{"version":3,"file":"SensorTagsSection.d.ts","sourceRoot":"","sources":["../../../../../src/core/components/ui/Sensors/SensorTagsSection.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAO9B,MAAM,WAAW,sBAAsB;IACrC,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,iBAAiB,CAAC,EAAE,MAAM,CAAA;CAC3B;AASD,UAAU,sBAAsB;IAC9B,IAAI,EAAE,MAAM,EAAE,CAAA;IACd,OAAO,CAAC,EAAE,MAAM,GAAG,WAAW,CAAA;IAC9B,KAAK,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;IACtC,QAAQ,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;IACzC,YAAY,CAAC,EAAE,sBAAsB,CAAA;CACtC;AAED,wBAAgB,iBAAiB,CAAC,EAAE,IAAI,EAAE,OAAgB,EAAE,KAAK,EAAE,QAAQ,EAAE,YAAY,EAAE,EAAE,sBAAsB,qBAmFlH"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../src/core/components/ui/Sensors/SensorTagsSection.tsx"],"sourcesContent":["'use client'\r\n\r\nimport * as React from 'react'\r\
|
|
1
|
+
{"version":3,"sources":["../../../../../src/core/components/ui/Sensors/SensorTagsSection.tsx"],"sourcesContent":["'use client'\r\n\r\nimport * as React from 'react'\r\nimport { Button } from '../Button'\r\nimport { Badge } from '../Badge'\r\nimport { Input } from '../Input'\r\nimport * as LR from 'lucide-react'\r\nimport { stringToColour } from '../../viewers/map/utils/stringToColour'\r\n\r\nexport interface SensorTagsTranslations {\r\n addTag?: string\r\n removeTag?: string\r\n cancel?: string\r\n newTagPlaceholder?: string\r\n}\r\n\r\nconst DEFAULT_TRANSLATIONS: Required<SensorTagsTranslations> = {\r\n addTag: 'Add tag',\r\n removeTag: 'Remove tag',\r\n cancel: 'Cancel',\r\n newTagPlaceholder: 'New tag…',\r\n}\r\n\r\ninterface SensorTagsSectionProps {\r\n tags: string[]\r\n variant?: 'edit' | 'read-only'\r\n onAdd?: (tag: string) => Promise<void>\r\n onDelete?: (tag: string) => Promise<void>\r\n translations?: SensorTagsTranslations\r\n}\r\n\r\nexport function SensorTagsSection({ tags, variant = 'edit', onAdd, onDelete, translations }: SensorTagsSectionProps) {\r\n const tr = { ...DEFAULT_TRANSLATIONS, ...translations }\r\n const [addingTag, setAddingTag] = React.useState(false)\r\n const [newTagValue, setNewTagValue] = React.useState('')\r\n\r\n const handleAdd = async () => {\r\n const trimmed = newTagValue.trim()\r\n if (!trimmed || !onAdd) return\r\n if (tags.includes(trimmed)) return\r\n await onAdd(trimmed)\r\n setNewTagValue('')\r\n setAddingTag(false)\r\n }\r\n\r\n if (variant === 'read-only') {\r\n if (tags.length === 0) return null\r\n return (\r\n <div className=\"flex items-center gap-2 px-4 py-2 border-t flex-wrap\">\r\n <span className=\"text-muted-foreground text-sm\">Tags:</span>\r\n {tags.map((tag, index) => {\r\n const tagColor = stringToColour(tag, 'max')\r\n return (\r\n <Badge key={index} variant=\"secondary\" className=\"text-xs\" style={{ backgroundColor: tagColor, color: 'white' }}>\r\n {tag}\r\n </Badge>\r\n )\r\n })}\r\n </div>\r\n )\r\n }\r\n\r\n return (\r\n <div className=\"flex items-center gap-2 px-4 py-2 border-t flex-wrap\">\r\n <span className=\"text-muted-foreground text-sm\">Tags:</span>\r\n {tags.length > 0 && (\r\n <>\r\n {tags.map((tag, index) => {\r\n const tagColor = stringToColour(tag, 'max')\r\n return (\r\n <Badge key={index} variant=\"secondary\" className=\"text-xs\" style={{ backgroundColor: tagColor, color: 'white' }}>\r\n {tag}{' '}\r\n {onDelete && (\r\n <span title={tr.removeTag}>\r\n <LR.X\r\n className=\"h-3 w-3 cursor-pointer ml-2 -mr-1\"\r\n onClick={() => onDelete(tag)}\r\n />\r\n </span>\r\n )}\r\n </Badge>\r\n )\r\n })}\r\n </>\r\n )}\r\n {onAdd && (\r\n addingTag ? (\r\n <div className=\"flex items-center gap-1 ml-auto\">\r\n <Input\r\n autoFocus\r\n value={newTagValue}\r\n onChange={e => setNewTagValue(e.target.value)}\r\n onKeyDown={e => {\r\n if (e.key === 'Enter') handleAdd()\r\n if (e.key === 'Escape') { setAddingTag(false); setNewTagValue('') }\r\n }}\r\n placeholder={tr.newTagPlaceholder}\r\n className=\"h-6 text-xs w-28 px-2\"\r\n />\r\n <Button title={tr.addTag} variant=\"ghost\" size=\"icon\" className=\"h-6 w-6 p-0\" onClick={handleAdd}>\r\n <LR.Plus className=\"h-3 w-3\" />\r\n </Button>\r\n <Button title={tr.cancel} variant=\"ghost\" size=\"icon\" className=\"h-6 w-6 p-0\" onClick={() => { setAddingTag(false); setNewTagValue('') }}>\r\n <LR.X className=\"h-3 w-3\" />\r\n </Button>\r\n </div>\r\n ) : (\r\n <Button title={tr.addTag} variant=\"ghost\" size=\"icon\" className=\"h-6 w-6 p-0 ml-auto\" onClick={() => setAddingTag(true)}>\r\n <LR.Plus className=\"h-3 w-3\" />\r\n </Button>\r\n )\r\n )}\r\n </div>\r\n )\r\n}\r\n"],"mappings":"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","names":[]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SensorsSection.d.ts","sourceRoot":"","sources":["../../../../../src/core/components/ui/Sensors/SensorsSection.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;
|
|
1
|
+
{"version":3,"file":"SensorsSection.d.ts","sourceRoot":"","sources":["../../../../../src/core/components/ui/Sensors/SensorsSection.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AA0B9B,6EAA6E;AAC7E,eAAO,MAAM,YAAY,iBAAiB,CAAA;AAE1C,wBAAgB,cAAc,sBAgR7B"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../src/core/components/ui/Sensors/SensorsSection.tsx"],"sourcesContent":["'use client'\r\n\r\nimport * as React from 'react'\r\nimport { BuildingsContext, MenusContext, ToolsContext } from '../../../store'\r\n// Audit Phase 1.F (F-2c): direct file imports — bypass ui barrel.\r\nimport { Button } from '../Button'\r\nimport { CollapsibleSection } from '../CollapsibleSection'\r\nimport { SearchInput } from '../SearchInput'\r\nimport { DropdownMenu } from '../DropdownMenu'\r\nimport {\r\n DropdownMenuTrigger,\r\n DropdownMenuContent,\r\n DropdownMenuLabel,\r\n DropdownMenuSeparator,\r\n DropdownMenuRadioGroup,\r\n DropdownMenuRadioItem,\r\n} from '../DropdownMenu'\r\nimport { useTranslations } from 'next-intl'\r\nimport { CollapsibleSensorItem } from './CollapsibleSensorItem'\r\nimport { SensorsSectionSkeleton } from './SensorsSectionSkeleton'\r\nimport { useSensor, useSensors } from '../../../hooks/sensors/sensors'\r\nimport { toast } from 'sonner'\r\nimport type { Sensor } from '../../../types/dbTypes';\r\nimport { useSensorTypes } from '../../../hooks/sensorTypes/sensorTypes'\r\nimport * as LR from 'lucide-react'\r\nimport { stringToColour } from '../../viewers/map/utils/stringToColour'\r\n\r\ntype SensorAction = 'view' | 'edit' | 'delete'\r\n\r\n/** Stable key used in state for sensors with no tags — locale-independent */\r\nexport const UNTAGGED_TAG = '__untagged__'\r\n\r\nexport function SensorsSection() {\r\n // Translation\r\n const t = useTranslations('SensorsSection')\r\n\r\n const { dispatch: toolsDispatch } = React.useContext(ToolsContext)\r\n const { state: menusState, dispatch: menusDispatch } = React.useContext(MenusContext)\r\n const { currentViewer, visibleSensorTypes, visibleSensorTags } = menusState.menus\r\n\r\n const { sensors: allSensors }: { sensors: Sensor[] } = useSensors()\r\n const { sensorTypes, isLoading } = useSensorTypes()\r\n\r\n const {state: buildingsState} = React.useContext(BuildingsContext)\r\n const buildingId = buildingsState?.buildings?.building?.id || -1\r\n \r\n const [sensorToDelete, setSensorToDelete] = React.useState<number | null>(null)\r\n const [searchQuery, setSearchQuery] = React.useState('')\r\n const [groupBy, setGroupBy] = React.useState<'type' | 'tag'>('type')\r\n const { deleteSensor } = useSensor(sensorToDelete)\r\n\r\n const handleAddSensor = React.useCallback((currentSensorTypeId?: number) => {\r\n if (!currentViewer) return\r\n toolsDispatch({\r\n type: 'SET-TOOL',\r\n payload: {\r\n currentToolId:\r\n currentViewer === 'bim'\r\n ? 'bim-add-sensor'\r\n : currentViewer === 'map'\r\n ? 'map-add-sensor'\r\n : undefined,\r\n },\r\n })\r\n if (currentSensorTypeId) {\r\n menusDispatch({\r\n type: 'SET_CURRENT_SENSOR_TYPE_ID',\r\n payload: { currentSensorTypeId },\r\n })\r\n }\r\n }, [toolsDispatch, currentViewer])\r\n \r\n const handleSensorAction = React.useCallback((action: SensorAction, id: number) => {\r\n switch (action) {\r\n case 'view':\r\n console.log(`View sensor ${id}`)\r\n break\r\n case 'edit':\r\n console.log(`Edit sensor ${id}`)\r\n break\r\n case 'delete':\r\n toast.success(t('sensorDeleted'))\r\n setSensorToDelete(id)\r\n break\r\n default:\r\n break\r\n }\r\n }, [t])\r\n\r\n // Trigger deletion when sensorToDelete changes\r\n React.useEffect(() => {\r\n if (sensorToDelete !== null) {\r\n deleteSensor()\r\n setSensorToDelete(null)\r\n }\r\n }, [sensorToDelete, deleteSensor])\r\n\r\n const currentSensors: Sensor[] = allSensors.filter((sensor) => currentViewer === sensor.viewer && (!sensor.buildingId || sensor.buildingId === buildingId))\r\n \r\n // Filter sensors based on search query\r\n const filteredSensors = React.useMemo(() => {\r\n if (!searchQuery.trim()) return currentSensors\r\n\r\n const query = searchQuery.toLowerCase()\r\n return currentSensors.filter((sensor) => {\r\n const sensorTypeName =\r\n sensorTypes.find(type => type.id === sensor.typeId)?.name ?? ''\r\n return (\r\n sensor.name?.toLowerCase().includes(query) ||\r\n sensorTypeName.toLowerCase().includes(query)\r\n )\r\n })\r\n }, [currentSensors, searchQuery])\r\n \r\n const toggleSensorTypeVisibility = React.useCallback((sensorTypeId: number) => {\r\n menusDispatch({\r\n type: 'TOGGLE_SENSOR_TYPE_VISIBILITY',\r\n payload: {\r\n viewer: currentViewer,\r\n sensorTypeId: sensorTypeId,\r\n },\r\n })\r\n }, [currentViewer, menusDispatch])\r\n\r\n const handleGroupByChange = React.useCallback((newGroupBy: 'type' | 'tag') => {\r\n setGroupBy(newGroupBy)\r\n // Clear the opposing visibility state so markers from the old filter don't bleed through\r\n if (newGroupBy === 'tag') {\r\n menusDispatch({ type: 'HIDE_ALL_SENSORS_IN_VIEWER', payload: { viewer: currentViewer } })\r\n } else {\r\n menusDispatch({ type: 'HIDE_ALL_SENSOR_TAGS_IN_VIEWER', payload: { viewer: currentViewer } })\r\n }\r\n }, [currentViewer, menusDispatch])\r\n\r\n const toggleSensorTagVisibility = React.useCallback((tag: string) => {\r\n menusDispatch({\r\n type: 'TOGGLE_SENSOR_TAG_VISIBILITY',\r\n payload: {\r\n viewer: currentViewer,\r\n sensorTag: tag,\r\n },\r\n })\r\n }, [currentViewer, menusDispatch])\r\n\r\n // Group sensors by type, using sensorTypes as the source of truth for types\r\n const sensorsByType = React.useMemo(() => {\r\n const grouped: Record<string, Sensor[]> = {}\r\n sensorTypes.forEach(type => {\r\n grouped[type.name] = []\r\n })\r\n filteredSensors.forEach(sensor => {\r\n const type = sensorTypes.find(t => t.id === sensor.typeId)\r\n const typeName = type?.name ?? 'Other'\r\n if (!grouped[typeName]) {\r\n grouped[typeName] = []\r\n }\r\n grouped[typeName].push(sensor)\r\n })\r\n // Remove empty groups\r\n Object.keys(grouped).forEach(key => {\r\n if (grouped[key].length === 0) {\r\n delete grouped[key]\r\n }\r\n })\r\n return grouped\r\n }, [filteredSensors, sensorTypes])\r\n\r\n // Group sensors by tag\r\n const sensorsByTag = React.useMemo(() => {\r\n const grouped: Record<string, Sensor[]> = {}\r\n filteredSensors.forEach(sensor => {\r\n const tags = sensor.tags && sensor.tags.length > 0 ? sensor.tags : [UNTAGGED_TAG]\r\n tags.forEach(tag => {\r\n if (!grouped[tag]) grouped[tag] = []\r\n grouped[tag].push(sensor)\r\n })\r\n })\r\n return grouped\r\n }, [filteredSensors])\r\n\r\n return (\r\n <div className=\"flex-1 flex flex-col space-y-4 overflow-hidden\">\r\n <div className=\"px-4 flex items-center gap-2\">\r\n <SearchInput\r\n placeholder={t('searchSensors')}\r\n value={searchQuery}\r\n onChange={e => setSearchQuery(e.target.value)}\r\n />\r\n <DropdownMenu>\r\n <DropdownMenuTrigger asChild>\r\n <Button variant=\"outline\" size=\"icon\" title={t('filters')} className=\"flex-shrink-0\">\r\n <LR.Funnel size={16} />\r\n </Button>\r\n </DropdownMenuTrigger>\r\n <DropdownMenuContent align=\"end\">\r\n <DropdownMenuLabel>{t('groupBy')}</DropdownMenuLabel>\r\n <DropdownMenuSeparator />\r\n <DropdownMenuRadioGroup value={groupBy} onValueChange={v => handleGroupByChange(v as 'type' | 'tag')}>\r\n <DropdownMenuRadioItem value=\"type\">\r\n <LR.Layers className=\"mr-2 h-4 w-4\" />\r\n {t('groupByType')}\r\n </DropdownMenuRadioItem>\r\n <DropdownMenuRadioItem value=\"tag\">\r\n <LR.Tag className=\"mr-2 h-4 w-4\" />\r\n {t('groupByTag')}\r\n </DropdownMenuRadioItem>\r\n </DropdownMenuRadioGroup>\r\n </DropdownMenuContent>\r\n </DropdownMenu>\r\n </div>\r\n {isLoading ? (\r\n <SensorsSectionSkeleton />\r\n ) : \r\n filteredSensors.length === 0 ? (\r\n <div className=\"p-4 text-muted-foreground text-sm flex flex-col items-center\">\r\n <div>{t('noSensorsFound')}</div>\r\n <Button\r\n className=\"mt-2 px-4 \"\r\n variant='outline'\r\n onClick={() => handleAddSensor()}\r\n >\r\n {t('addSensorTitle')}\r\n </Button>\r\n </div>\r\n ) : groupBy === 'type' ? (\r\n Object.entries(sensorsByType).map(([typeName, sensorsOfType]) => {\r\n const type = sensorTypes.find(t => t.name === typeName)\r\n const typeId = type?.id ?? -1\r\n\r\n const viewerTypes = visibleSensorTypes?.[currentViewer]\r\n const isTypeVisible = viewerTypes?.length > 0 && viewerTypes.includes(typeId)\r\n\r\n const sensorIcon = LR[type?.icon] || LR.Radio\r\n\r\n return (\r\n <CollapsibleSection\r\n key={typeName}\r\n title={typeName.replace(/_/g, ' ')}\r\n icon={sensorIcon}\r\n className=\"overflow-y-auto\"\r\n itemCount={sensorsOfType.length}\r\n onAddItem={() => handleAddSensor(typeId)}\r\n addItemTitle={t('addSensorTitle')}\r\n defaultOpen={false}\r\n switchVariant={{\r\n checked: isTypeVisible,\r\n onCheckedChange: () => toggleSensorTypeVisibility(typeId),\r\n }}\r\n >\r\n <div className=\"space-y-2 mx-2 pr-2\">\r\n {sensorsOfType.map((sensor) => (\r\n <CollapsibleSensorItem\r\n key={sensor.id}\r\n sensor={sensor}\r\n sensorType={type}\r\n onAction={handleSensorAction}\r\n isVisible={isTypeVisible}\r\n onMouseEnter={() => menusDispatch({ type: 'SET_CURRENT_SENSOR_ID', payload: { currentSensorId: sensor.id } })}\r\n onMouseLeave={() => menusDispatch({ type: 'SET_CURRENT_SENSOR_ID', payload: { currentSensorId: null } })}\r\n />\r\n ))}\r\n </div>\r\n </CollapsibleSection>\r\n )\r\n })\r\n ) : (\r\n Object.entries(sensorsByTag).map(([tag, sensorsOfTag]) => {\r\n const isTagVisible = visibleSensorTags?.[currentViewer]?.includes(tag) ?? false\r\n return (\r\n <CollapsibleSection\r\n key={tag}\r\n title={tag === UNTAGGED_TAG ? t('untagged') : tag}\r\n icon={LR.Tag}\r\n colour={stringToColour(tag, 'max')}\r\n className=\"overflow-y-auto\"\r\n itemCount={sensorsOfTag.length}\r\n defaultOpen={false}\r\n switchVariant={{\r\n checked: isTagVisible,\r\n onCheckedChange: () => toggleSensorTagVisibility(tag),\r\n }}\r\n >\r\n <div className=\"space-y-2 mx-2 pr-2\">\r\n {sensorsOfTag.map((sensor) => {\r\n const type = sensorTypes.find(tp => tp.id === sensor.typeId)\r\n return (\r\n <CollapsibleSensorItem\r\n key={sensor.id}\r\n sensor={sensor}\r\n sensorType={type}\r\n onAction={handleSensorAction}\r\n isVisible={isTagVisible}\r\n onMouseEnter={() => menusDispatch({ type: 'SET_CURRENT_SENSOR_ID', payload: { currentSensorId: sensor.id } })}\r\n onMouseLeave={() => menusDispatch({ type: 'SET_CURRENT_SENSOR_ID', payload: { currentSensorId: null } })}\r\n />\r\n )\r\n })}\r\n </div>\r\n </CollapsibleSection>\r\n )\r\n })\r\n )}\r\n </div>\r\n )\r\n}\r\n"],"mappings":"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","names":["_a","_b","t"]}
|
|
1
|
+
{"version":3,"sources":["../../../../../src/core/components/ui/Sensors/SensorsSection.tsx"],"sourcesContent":["'use client'\r\n\r\nimport * as React from 'react'\r\nimport { BuildingsContext, MenusContext, ToolsContext } from '../../../store'\r\nimport { Button } from '../Button'\r\nimport { CollapsibleSection } from '../CollapsibleSection'\r\nimport { SearchInput } from '../SearchInput'\r\nimport { DropdownMenu } from '../DropdownMenu'\r\nimport {\r\n DropdownMenuTrigger,\r\n DropdownMenuContent,\r\n DropdownMenuLabel,\r\n DropdownMenuSeparator,\r\n DropdownMenuRadioGroup,\r\n DropdownMenuRadioItem,\r\n} from '../DropdownMenu'\r\nimport { useTranslations } from 'next-intl'\r\nimport { CollapsibleSensorItem } from './CollapsibleSensorItem'\r\nimport { SensorsSectionSkeleton } from './SensorsSectionSkeleton'\r\nimport { useSensor, useSensors } from '../../../hooks/sensors/sensors'\r\nimport { toast } from 'sonner'\r\nimport type { Sensor } from '../../../types/dbTypes';\r\nimport { useSensorTypes } from '../../../hooks/sensorTypes/sensorTypes'\r\nimport * as LR from 'lucide-react'\r\nimport { stringToColour } from '../../viewers/map/utils/stringToColour'\r\n\r\ntype SensorAction = 'view' | 'edit' | 'delete'\r\n\r\n/** Stable key used in state for sensors with no tags — locale-independent */\r\nexport const UNTAGGED_TAG = '__untagged__'\r\n\r\nexport function SensorsSection() {\r\n // Translation\r\n const t = useTranslations('SensorsSection')\r\n\r\n const { dispatch: toolsDispatch } = React.useContext(ToolsContext)\r\n const { state: menusState, dispatch: menusDispatch } = React.useContext(MenusContext)\r\n const { currentViewer, visibleSensorTypes, visibleSensorTags } = menusState.menus\r\n\r\n const { sensors: allSensors }: { sensors: Sensor[] } = useSensors()\r\n const { sensorTypes, isLoading } = useSensorTypes()\r\n\r\n const {state: buildingsState} = React.useContext(BuildingsContext)\r\n const buildingId = buildingsState?.buildings?.building?.id || -1\r\n \r\n const [sensorToDelete, setSensorToDelete] = React.useState<number | null>(null)\r\n const [searchQuery, setSearchQuery] = React.useState('')\r\n const [groupBy, setGroupBy] = React.useState<'type' | 'tag'>('type')\r\n const { deleteSensor } = useSensor(sensorToDelete)\r\n\r\n const handleAddSensor = React.useCallback((currentSensorTypeId?: number) => {\r\n if (!currentViewer) return\r\n toolsDispatch({\r\n type: 'SET-TOOL',\r\n payload: {\r\n currentToolId:\r\n currentViewer === 'bim'\r\n ? 'bim-add-sensor'\r\n : currentViewer === 'map'\r\n ? 'map-add-sensor'\r\n : undefined,\r\n },\r\n })\r\n if (currentSensorTypeId) {\r\n menusDispatch({\r\n type: 'SET_CURRENT_SENSOR_TYPE_ID',\r\n payload: { currentSensorTypeId },\r\n })\r\n }\r\n }, [toolsDispatch, currentViewer])\r\n \r\n const handleSensorAction = React.useCallback((action: SensorAction, id: number) => {\r\n switch (action) {\r\n case 'view':\r\n console.log(`View sensor ${id}`)\r\n break\r\n case 'edit':\r\n console.log(`Edit sensor ${id}`)\r\n break\r\n case 'delete':\r\n toast.success(t('sensorDeleted'))\r\n setSensorToDelete(id)\r\n break\r\n default:\r\n break\r\n }\r\n }, [t])\r\n\r\n // Trigger deletion when sensorToDelete changes\r\n React.useEffect(() => {\r\n if (sensorToDelete !== null) {\r\n deleteSensor()\r\n setSensorToDelete(null)\r\n }\r\n }, [sensorToDelete, deleteSensor])\r\n\r\n const currentSensors: Sensor[] = allSensors.filter((sensor) => currentViewer === sensor.viewer && (!sensor.buildingId || sensor.buildingId === buildingId))\r\n \r\n // Filter sensors based on search query\r\n const filteredSensors = React.useMemo(() => {\r\n if (!searchQuery.trim()) return currentSensors\r\n\r\n const query = searchQuery.toLowerCase()\r\n return currentSensors.filter((sensor) => {\r\n const sensorTypeName =\r\n sensorTypes.find(type => type.id === sensor.typeId)?.name ?? ''\r\n return (\r\n sensor.name?.toLowerCase().includes(query) ||\r\n sensorTypeName.toLowerCase().includes(query)\r\n )\r\n })\r\n }, [currentSensors, searchQuery])\r\n \r\n const toggleSensorTypeVisibility = React.useCallback((sensorTypeId: number) => {\r\n menusDispatch({\r\n type: 'TOGGLE_SENSOR_TYPE_VISIBILITY',\r\n payload: {\r\n viewer: currentViewer,\r\n sensorTypeId: sensorTypeId,\r\n },\r\n })\r\n }, [currentViewer, menusDispatch])\r\n\r\n const handleGroupByChange = React.useCallback((newGroupBy: 'type' | 'tag') => {\r\n setGroupBy(newGroupBy)\r\n // Clear the opposing visibility state so markers from the old filter don't bleed through\r\n if (newGroupBy === 'tag') {\r\n menusDispatch({ type: 'HIDE_ALL_SENSORS_IN_VIEWER', payload: { viewer: currentViewer } })\r\n } else {\r\n menusDispatch({ type: 'HIDE_ALL_SENSOR_TAGS_IN_VIEWER', payload: { viewer: currentViewer } })\r\n }\r\n }, [currentViewer, menusDispatch])\r\n\r\n const toggleSensorTagVisibility = React.useCallback((tag: string) => {\r\n menusDispatch({\r\n type: 'TOGGLE_SENSOR_TAG_VISIBILITY',\r\n payload: {\r\n viewer: currentViewer,\r\n sensorTag: tag,\r\n },\r\n })\r\n }, [currentViewer, menusDispatch])\r\n\r\n // Group sensors by type, using sensorTypes as the source of truth for types\r\n const sensorsByType = React.useMemo(() => {\r\n const grouped: Record<string, Sensor[]> = {}\r\n sensorTypes.forEach(type => {\r\n grouped[type.name] = []\r\n })\r\n filteredSensors.forEach(sensor => {\r\n const type = sensorTypes.find(t => t.id === sensor.typeId)\r\n const typeName = type?.name ?? 'Other'\r\n if (!grouped[typeName]) {\r\n grouped[typeName] = []\r\n }\r\n grouped[typeName].push(sensor)\r\n })\r\n // Remove empty groups\r\n Object.keys(grouped).forEach(key => {\r\n if (grouped[key].length === 0) {\r\n delete grouped[key]\r\n }\r\n })\r\n return grouped\r\n }, [filteredSensors, sensorTypes])\r\n\r\n // Group sensors by tag\r\n const sensorsByTag = React.useMemo(() => {\r\n const grouped: Record<string, Sensor[]> = {}\r\n filteredSensors.forEach(sensor => {\r\n const tags = sensor.tags && sensor.tags.length > 0 ? sensor.tags : [UNTAGGED_TAG]\r\n tags.forEach(tag => {\r\n if (!grouped[tag]) grouped[tag] = []\r\n grouped[tag].push(sensor)\r\n })\r\n })\r\n return grouped\r\n }, [filteredSensors])\r\n\r\n return (\r\n <div className=\"flex-1 flex flex-col space-y-4 overflow-hidden\">\r\n <div className=\"px-4 flex items-center gap-2\">\r\n <SearchInput\r\n placeholder={t('searchSensors')}\r\n value={searchQuery}\r\n onChange={e => setSearchQuery(e.target.value)}\r\n />\r\n <DropdownMenu>\r\n <DropdownMenuTrigger asChild>\r\n <Button variant=\"outline\" size=\"icon\" title={t('filters')} className=\"flex-shrink-0\">\r\n <LR.Funnel size={16} />\r\n </Button>\r\n </DropdownMenuTrigger>\r\n <DropdownMenuContent align=\"end\">\r\n <DropdownMenuLabel>{t('groupBy')}</DropdownMenuLabel>\r\n <DropdownMenuSeparator />\r\n <DropdownMenuRadioGroup value={groupBy} onValueChange={v => handleGroupByChange(v as 'type' | 'tag')}>\r\n <DropdownMenuRadioItem value=\"type\">\r\n <LR.Layers className=\"mr-2 h-4 w-4\" />\r\n {t('groupByType')}\r\n </DropdownMenuRadioItem>\r\n <DropdownMenuRadioItem value=\"tag\">\r\n <LR.Tag className=\"mr-2 h-4 w-4\" />\r\n {t('groupByTag')}\r\n </DropdownMenuRadioItem>\r\n </DropdownMenuRadioGroup>\r\n </DropdownMenuContent>\r\n </DropdownMenu>\r\n </div>\r\n {isLoading ? (\r\n <SensorsSectionSkeleton />\r\n ) : \r\n filteredSensors.length === 0 ? (\r\n <div className=\"p-4 text-muted-foreground text-sm flex flex-col items-center\">\r\n <div>{t('noSensorsFound')}</div>\r\n <Button\r\n className=\"mt-2 px-4 \"\r\n variant='outline'\r\n onClick={() => handleAddSensor()}\r\n >\r\n {t('addSensorTitle')}\r\n </Button>\r\n </div>\r\n ) : groupBy === 'type' ? (\r\n Object.entries(sensorsByType).map(([typeName, sensorsOfType]) => {\r\n const type = sensorTypes.find(t => t.name === typeName)\r\n const typeId = type?.id ?? -1\r\n\r\n const viewerTypes = visibleSensorTypes?.[currentViewer]\r\n const isTypeVisible = viewerTypes?.length > 0 && viewerTypes.includes(typeId)\r\n\r\n const sensorIcon = LR[type?.icon] || LR.Radio\r\n\r\n return (\r\n <CollapsibleSection\r\n key={typeName}\r\n title={typeName.replace(/_/g, ' ')}\r\n icon={sensorIcon}\r\n className=\"overflow-y-auto\"\r\n itemCount={sensorsOfType.length}\r\n onAddItem={() => handleAddSensor(typeId)}\r\n addItemTitle={t('addSensorTitle')}\r\n defaultOpen={false}\r\n switchVariant={{\r\n checked: isTypeVisible,\r\n onCheckedChange: () => toggleSensorTypeVisibility(typeId),\r\n }}\r\n >\r\n <div className=\"space-y-2 mx-2 pr-2\">\r\n {sensorsOfType.map((sensor) => (\r\n <CollapsibleSensorItem\r\n key={sensor.id}\r\n sensor={sensor}\r\n sensorType={type}\r\n onAction={handleSensorAction}\r\n isVisible={isTypeVisible}\r\n onMouseEnter={() => menusDispatch({ type: 'SET_CURRENT_SENSOR_ID', payload: { currentSensorId: sensor.id } })}\r\n onMouseLeave={() => menusDispatch({ type: 'SET_CURRENT_SENSOR_ID', payload: { currentSensorId: null } })}\r\n />\r\n ))}\r\n </div>\r\n </CollapsibleSection>\r\n )\r\n })\r\n ) : (\r\n Object.entries(sensorsByTag).map(([tag, sensorsOfTag]) => {\r\n const isTagVisible = visibleSensorTags?.[currentViewer]?.includes(tag) ?? false\r\n return (\r\n <CollapsibleSection\r\n key={tag}\r\n title={tag === UNTAGGED_TAG ? t('untagged') : tag}\r\n icon={LR.Tag}\r\n colour={stringToColour(tag, 'max')}\r\n className=\"overflow-y-auto\"\r\n itemCount={sensorsOfTag.length}\r\n defaultOpen={false}\r\n switchVariant={{\r\n checked: isTagVisible,\r\n onCheckedChange: () => toggleSensorTagVisibility(tag),\r\n }}\r\n >\r\n <div className=\"space-y-2 mx-2 pr-2\">\r\n {sensorsOfTag.map((sensor) => {\r\n const type = sensorTypes.find(tp => tp.id === sensor.typeId)\r\n return (\r\n <CollapsibleSensorItem\r\n key={sensor.id}\r\n sensor={sensor}\r\n sensorType={type}\r\n onAction={handleSensorAction}\r\n isVisible={isTagVisible}\r\n onMouseEnter={() => menusDispatch({ type: 'SET_CURRENT_SENSOR_ID', payload: { currentSensorId: sensor.id } })}\r\n onMouseLeave={() => menusDispatch({ type: 'SET_CURRENT_SENSOR_ID', payload: { currentSensorId: null } })}\r\n />\r\n )\r\n })}\r\n </div>\r\n </CollapsibleSection>\r\n )\r\n })\r\n )}\r\n </div>\r\n )\r\n}\r\n"],"mappings":"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","names":["_a","_b","t"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ShareToolSubmenu.d.ts","sourceRoot":"","sources":["../../../../../src/core/components/ui/ShareFeature/ShareToolSubmenu.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAK/B,OAAO,EAAE,IAAI,EAAE,MAAM,sBAAsB,CAAA;
|
|
1
|
+
{"version":3,"file":"ShareToolSubmenu.d.ts","sourceRoot":"","sources":["../../../../../src/core/components/ui/ShareFeature/ShareToolSubmenu.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAK/B,OAAO,EAAE,IAAI,EAAE,MAAM,sBAAsB,CAAA;AAiC3C,UAAU,qBAAqB;IAC7B,IAAI,EAAE,IAAI,CAAA;IACV,iBAAiB,EAAE,MAAM,OAAO,CAAC,MAAM,CAAC,CAAA;IACxC,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB,cAAc,CAAC,EAAE,MAAM,CAAA;CACxB;AAED,eAAO,MAAM,gBAAgB,EAAE,KAAK,CAAC,EAAE,CAAC,qBAAqB,CA2S5D,CAAA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../src/core/components/ui/ShareFeature/ShareToolSubmenu.tsx"],"sourcesContent":["'use client'\r\n\r\nimport * as React from \"react\";\r\nimport { useRouter, usePathname } from 'next/navigation'\r\nimport { useTranslations } from 'next-intl'\r\n\r\n// Utilities\r\nimport { Tool } from '../../../types/tools'\r\nimport { ViewerNames } from '../../../types/'\r\n\r\n// Shadcn components\r\nimport {\r\n DropdownMenuItem,\r\n} from '../DropdownMenu'\r\nimport {\r\n Dialog,\r\n DialogContent,\r\n DialogHeader,\r\n DialogTitle,\r\n DialogTrigger,\r\n} from '../Dialog'\r\nimport { Input } from '../Input'\r\nimport { Button } from '../Button'\r\nimport { toast } from 'sonner'\r\n\r\n// Icons\r\nimport {\r\n Link,\r\n ScanQrCode,\r\n Camera,\r\n ChevronDown,\r\n} from 'lucide-react'\r\n\r\n// Audit Phase 1.B (F-jspdf): jsPDF and its transitive html2canvas\r\n// (~150 KB gzipped combined) used to be statically imported here.\r\n// ShareToolSubmenu is rendered on the map/BIM/PointCloud toolbar, so\r\n// the static import shipped jspdf on every page load even though most\r\n// share interactions don't choose PDF format. Loading it inside the\r\n// PDF branch defers the chunk until the user actually picks PDF.\r\n\r\n// Custom components\r\nimport QRCodeWithLink from './QRCodeWithLink'\r\nimport { ToolbarSubmenu } from '../../ToolbarSubmenu'\r\nimport { LoadingSpinner } from '../LoadingSpinner'\r\n\r\nimport { MenusContext, MapContext, BimContext } from '../../../store'\r\n\r\ninterface ShareToolSubmenuProps {\r\n tool: Tool\r\n constructShareUrl: () => Promise<string>\r\n translationKey?: string\r\n dialogTitleKey?: string\r\n}\r\n\r\nexport const ShareToolSubmenu: React.FC<ShareToolSubmenuProps> = ({ \r\n tool, \r\n constructShareUrl, \r\n translationKey = 'ShareTool',\r\n dialogTitleKey\r\n}) => {\r\n // Translation\r\n \r\n const t = useTranslations(\"ShareTool\")\r\n // Get current viewer from context\r\n const { state: menusState } = React.useContext(MenusContext)\r\n const { currentViewer } = menusState.menus\r\n\r\n // Get map instance from context\r\n const { state: mapState } = React.useContext(MapContext)\r\n const { map } = mapState.map\r\n\r\n const { state: bimState } = React.useContext(BimContext)\r\n const { world } = bimState.bim\r\n\r\n // route and navigation\r\n const router = useRouter()\r\n const pathname = usePathname()\r\n\r\n // sharing\r\n const [shareUrlLink, setShareUrlLink] = React.useState<string>('')\r\n const [origin, setOrigin] = React.useState<string>('')\r\n\r\n const [isLoadingShareLink, setIsLoadingShareLink] = React.useState(false)\r\n const [isLoadingQRCode, setIsLoadingQRCode] = React.useState(false)\r\n\r\n const [qrDialogOpen, setQrDialogOpen] = React.useState(false)\r\n\r\n // screenshot state\r\n const [screenshotDialogOpen, setScreenshotDialogOpen] = React.useState(false)\r\n const [screenshotBlob, setScreenshotBlob] = React.useState<Blob | null>(null)\r\n const [screenshotUrl, setScreenshotUrl] = React.useState<string>('')\r\n const [screenshotName, setScreenshotName] = React.useState<string>('')\r\n const [exportFormat, setExportFormat] = React.useState<'png' | 'pdf'>('png')\r\n\r\n // set initial origin and url link\r\n React.useEffect(() => {\r\n setOrigin(window.location.origin)\r\n setShareUrlLink(window.location.origin + pathname)\r\n }, [pathname])\r\n\r\n // Cleanup screenshot URL when dialog closes\r\n React.useEffect(() => {\r\n if (!screenshotDialogOpen && screenshotUrl) {\r\n URL.revokeObjectURL(screenshotUrl)\r\n setScreenshotUrl('')\r\n setScreenshotBlob(null)\r\n }\r\n }, [screenshotDialogOpen, screenshotUrl])\r\n\r\n // Get current view information and copy to clipboard\r\n const handleClickShare = async () => {\r\n setIsLoadingShareLink(true)\r\n try {\r\n const shareUrl = await constructShareUrl()\r\n const writeToClipBoard = () => {\r\n try {\r\n navigator.clipboard.writeText(shareUrl)// this may causes error\r\n }\r\n catch {}\r\n }\r\n writeToClipBoard()\r\n router.push(shareUrl, { scroll: false }) // push but do not refresh\r\n }\r\n catch (error) {\r\n console.error('Failed to copy:', error)\r\n }\r\n finally {\r\n setIsLoadingShareLink(false)\r\n }\r\n toast.success(t('copySuccess'))\r\n }\r\n\r\n // Get current view information and show QR dialog\r\n const handleShareQR = async () => {\r\n setIsLoadingQRCode(true)\r\n try {\r\n const shareUrl = await constructShareUrl()\r\n setShareUrlLink(shareUrl)\r\n router.push(shareUrl, { scroll: false }) // push but do not refresh\r\n setQrDialogOpen(true)\r\n }\r\n catch (error) {\r\n console.error('Failed to generate QR:', error)\r\n }\r\n finally {\r\n setIsLoadingQRCode(false)\r\n }\r\n }\r\n\r\n const handleClickScreenshot = async () => {\r\n try {\r\n let canvas: HTMLCanvasElement | null = null\r\n\r\n // Access to the map instance for map viewer\r\n if (currentViewer === ViewerNames.map && map) {\r\n // Use the map's 'render' event to capture after render completes\r\n const captureAfterRender = () => {\r\n const mapCanvas = map.getCanvas()\r\n mapCanvas.toBlob((blob) => {\r\n if (!blob) return\r\n \r\n const url = URL.createObjectURL(blob)\r\n setScreenshotBlob(blob)\r\n setScreenshotUrl(url)\r\n setScreenshotName(`screenshot-${currentViewer}-${Date.now()}`)\r\n setScreenshotDialogOpen(true)\r\n }, 'image/png', 1.0)\r\n }\r\n\r\n // Listen for the next render event\r\n map.once('render', captureAfterRender)\r\n // Trigger the repaint\r\n map.triggerRepaint()\r\n return\r\n }\r\n \r\n // For BIM viewer, force a render then capture\r\n if (currentViewer === ViewerNames.bim && world?.renderer && world?.scene && world?.camera) {\r\n // Force Three.js to render\r\n world.renderer.three.render(world.scene.three, world.camera.three)\r\n \r\n // Get the canvas and capture immediately after render\r\n canvas = document.querySelector('#bim-viewer-container canvas')\r\n if (canvas) {\r\n canvas.toBlob((blob) => {\r\n if (!blob) return\r\n \r\n const url = URL.createObjectURL(blob)\r\n setScreenshotBlob(blob)\r\n setScreenshotUrl(url)\r\n setScreenshotName(`screenshot-${currentViewer}-${Date.now()}`)\r\n setScreenshotDialogOpen(true)\r\n }, 'image/png', 1.0)\r\n }\r\n return\r\n }\r\n\r\n if (currentViewer === ViewerNames.pointcloud) {\r\n canvas = document.querySelector('#pointcloud-viewer-container canvas')\r\n } else {\r\n // Fallback to first canvas\r\n canvas = document.querySelector('canvas')\r\n }\r\n\r\n if (!canvas) {\r\n throw new Error('No canvas found')\r\n }\r\n canvas.toBlob((blob) => {\r\n if (!blob) return\r\n \r\n const url = URL.createObjectURL(blob)\r\n setScreenshotBlob(blob)\r\n setScreenshotUrl(url)\r\n setScreenshotName(`screenshot-${currentViewer}-${Date.now()}`)\r\n setScreenshotDialogOpen(true)\r\n }, 'image/png', 1.0)\r\n \r\n } catch (error) {\r\n console.error('Screenshot failed:', error)\r\n }\r\n toast.success(t('ssSuccess'))\r\n }\r\n\r\n const handleDownloadScreenshot = async () => {\r\n if (!screenshotUrl) return\r\n\r\n if (exportFormat === 'pdf') {\r\n // F-jspdf: dynamic-import the PDF library only when the user picks PDF.\r\n const { default: jsPDF } = await import('jspdf')\r\n // Create PDF with the screenshot\r\n const img = new Image()\r\n img.onload = () => {\r\n // Calculate dimensions to fit on page while maintaining aspect ratio\r\n const pdf = new jsPDF({\r\n orientation: img.width > img.height ? 'landscape' : 'portrait',\r\n unit: 'px',\r\n format: [img.width, img.height]\r\n })\r\n pdf.addImage(screenshotUrl, 'PNG', 0, 0, img.width, img.height)\r\n pdf.save(`${screenshotName}.pdf`)\r\n setScreenshotDialogOpen(false)\r\n }\r\n img.src = screenshotUrl\r\n } else {\r\n // Download as PNG\r\n const a = document.createElement('a')\r\n a.href = screenshotUrl\r\n a.download = `${screenshotName}.png`\r\n a.click()\r\n setScreenshotDialogOpen(false)\r\n }\r\n }\r\n\r\n // Check if current viewer supports screenshots\r\n const canTakeScreenshot = [\r\n ViewerNames.map, \r\n ViewerNames.bim, \r\n ViewerNames.pointcloud\r\n ].includes(currentViewer as ViewerNames)\r\n\r\n return (\r\n <ToolbarSubmenu tool={tool}>\r\n <Dialog open={qrDialogOpen} onOpenChange={setQrDialogOpen}>\r\n <DropdownMenuItem onClick={(e) => { e.preventDefault() }} className=\"cursor-pointer\">\r\n <DialogTrigger asChild>\r\n <button onClick={handleShareQR} disabled={isLoadingQRCode} className=\"flex w-full gap-2 items-center\">\r\n {isLoadingQRCode ? <LoadingSpinner /> : <ScanQrCode size={16} />}\r\n <span>{isLoadingQRCode ? t('generating') : t('generate')}</span>\r\n </button>\r\n </DialogTrigger>\r\n </DropdownMenuItem>\r\n\r\n <DialogContent className=\"flex flex-col items-center gap-4 max-w-sm\">\r\n <DialogHeader>\r\n <DialogTitle>\r\n {dialogTitleKey === 'map' \r\n ? t('mapDialogTitle') \r\n : dialogTitleKey === 'bim' \r\n ? t('bimDialogTitle') \r\n : t('pointCloudDialogTitle')}\r\n </DialogTitle>\r\n </DialogHeader>\r\n <QRCodeWithLink title=\"\" urlToShare={shareUrlLink} isLoadingState={isLoadingQRCode} />\r\n </DialogContent>\r\n </Dialog>\r\n\r\n {/* Copy to clipboard */}\r\n <DropdownMenuItem onClick={handleClickShare} disabled={isLoadingShareLink} onSelect={(e) => { e.preventDefault() }} className=\"cursor-pointer\">\r\n <div className=\"flex items-center w-full gap-2\">\r\n {isLoadingShareLink ? <LoadingSpinner /> : <Link size={16} />}\r\n <span>{isLoadingShareLink ? t('copying') : t('copy')}</span>\r\n </div>\r\n </DropdownMenuItem>\r\n\r\n {/* Screenshot */}\r\n {canTakeScreenshot && (\r\n <Dialog open={screenshotDialogOpen} onOpenChange={setScreenshotDialogOpen}>\r\n <DropdownMenuItem onClick={(e) => { e.preventDefault() }} className=\"cursor-pointer\">\r\n <DialogTrigger asChild>\r\n <button onClick={handleClickScreenshot} className=\"flex w-full gap-2 items-center\">\r\n <Camera size={16}/>\r\n <span>{t('ss')}</span>\r\n </button>\r\n </DialogTrigger>\r\n </DropdownMenuItem>\r\n\r\n <DialogContent>\r\n <DialogHeader>\r\n <DialogTitle>{t('ssDownload')}</DialogTitle>\r\n </DialogHeader>\r\n \r\n <div className=\"flex flex-col gap-4\">\r\n {screenshotUrl && (\r\n <img \r\n src={screenshotUrl} \r\n alt=\"Screenshot preview\" \r\n className=\"w-full h-auto\"\r\n />\r\n )}\r\n \r\n <div className=\"flex flex-col gap-2\">\r\n <label htmlFor=\"screenshot-name\">\r\n {t('fileName')}\r\n </label>\r\n <Input\r\n id=\"screenshot-name\"\r\n value={screenshotName}\r\n onChange={(e) => setScreenshotName(e.target.value)}\r\n />\r\n </div>\r\n\r\n <div className=\"flex flex-col gap-2\">\r\n <label htmlFor=\"export-format\">\r\n {t('format')}\r\n </label>\r\n <select\r\n id=\"export-format\"\r\n value={exportFormat}\r\n onChange={(e) => setExportFormat(e.target.value as 'png' | 'pdf')}\r\n className=\"flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2\"\r\n >\r\n <option value=\"png\">{t('png')}</option>\r\n <option value=\"pdf\">{t('pdf')}</option>\r\n </select>\r\n </div>\r\n\r\n <Button onClick={handleDownloadScreenshot}>\r\n {t('download')}\r\n </Button>\r\n </div>\r\n </DialogContent>\r\n </Dialog>\r\n )}\r\n </ToolbarSubmenu>\r\n )\r\n}"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../../../../src/core/components/ui/ShareFeature/ShareToolSubmenu.tsx"],"sourcesContent":["'use client'\r\n\r\nimport * as React from \"react\";\r\nimport { useRouter, usePathname } from 'next/navigation'\r\nimport { useTranslations } from 'next-intl'\r\n\r\n// Utilities\r\nimport { Tool } from '../../../types/tools'\r\nimport { ViewerNames } from '../../../types/'\r\n\r\n// Shadcn components\r\nimport {\r\n DropdownMenuItem,\r\n} from '../DropdownMenu'\r\nimport {\r\n Dialog,\r\n DialogContent,\r\n DialogHeader,\r\n DialogTitle,\r\n DialogTrigger,\r\n} from '../Dialog'\r\nimport { Input } from '../Input'\r\nimport { Button } from '../Button'\r\nimport { toast } from 'sonner'\r\n\r\n// Icons\r\nimport {\r\n Link,\r\n ScanQrCode,\r\n Camera,\r\n ChevronDown,\r\n} from 'lucide-react'\r\n\r\n// Custom components\r\nimport QRCodeWithLink from './QRCodeWithLink'\r\nimport { ToolbarSubmenu } from '../../ToolbarSubmenu'\r\nimport { LoadingSpinner } from '../LoadingSpinner'\r\n\r\nimport { MenusContext, MapContext, BimContext } from '../../../store'\r\n\r\ninterface ShareToolSubmenuProps {\r\n tool: Tool\r\n constructShareUrl: () => Promise<string>\r\n translationKey?: string\r\n dialogTitleKey?: string\r\n}\r\n\r\nexport const ShareToolSubmenu: React.FC<ShareToolSubmenuProps> = ({ \r\n tool, \r\n constructShareUrl, \r\n translationKey = 'ShareTool',\r\n dialogTitleKey\r\n}) => {\r\n // Translation\r\n \r\n const t = useTranslations(\"ShareTool\")\r\n // Get current viewer from context\r\n const { state: menusState } = React.useContext(MenusContext)\r\n const { currentViewer } = menusState.menus\r\n\r\n // Get map instance from context\r\n const { state: mapState } = React.useContext(MapContext)\r\n const { map } = mapState.map\r\n\r\n const { state: bimState } = React.useContext(BimContext)\r\n const { world } = bimState.bim\r\n\r\n // route and navigation\r\n const router = useRouter()\r\n const pathname = usePathname()\r\n\r\n // sharing\r\n const [shareUrlLink, setShareUrlLink] = React.useState<string>('')\r\n const [origin, setOrigin] = React.useState<string>('')\r\n\r\n const [isLoadingShareLink, setIsLoadingShareLink] = React.useState(false)\r\n const [isLoadingQRCode, setIsLoadingQRCode] = React.useState(false)\r\n\r\n const [qrDialogOpen, setQrDialogOpen] = React.useState(false)\r\n\r\n // screenshot state\r\n const [screenshotDialogOpen, setScreenshotDialogOpen] = React.useState(false)\r\n const [screenshotBlob, setScreenshotBlob] = React.useState<Blob | null>(null)\r\n const [screenshotUrl, setScreenshotUrl] = React.useState<string>('')\r\n const [screenshotName, setScreenshotName] = React.useState<string>('')\r\n const [exportFormat, setExportFormat] = React.useState<'png' | 'pdf'>('png')\r\n\r\n // set initial origin and url link\r\n React.useEffect(() => {\r\n setOrigin(window.location.origin)\r\n setShareUrlLink(window.location.origin + pathname)\r\n }, [pathname])\r\n\r\n // Cleanup screenshot URL when dialog closes\r\n React.useEffect(() => {\r\n if (!screenshotDialogOpen && screenshotUrl) {\r\n URL.revokeObjectURL(screenshotUrl)\r\n setScreenshotUrl('')\r\n setScreenshotBlob(null)\r\n }\r\n }, [screenshotDialogOpen, screenshotUrl])\r\n\r\n // Get current view information and copy to clipboard\r\n const handleClickShare = async () => {\r\n setIsLoadingShareLink(true)\r\n try {\r\n const shareUrl = await constructShareUrl()\r\n const writeToClipBoard = () => {\r\n try {\r\n navigator.clipboard.writeText(shareUrl)// this may causes error\r\n }\r\n catch {}\r\n }\r\n writeToClipBoard()\r\n router.push(shareUrl, { scroll: false }) // push but do not refresh\r\n }\r\n catch (error) {\r\n console.error('Failed to copy:', error)\r\n }\r\n finally {\r\n setIsLoadingShareLink(false)\r\n }\r\n toast.success(t('copySuccess'))\r\n }\r\n\r\n // Get current view information and show QR dialog\r\n const handleShareQR = async () => {\r\n setIsLoadingQRCode(true)\r\n try {\r\n const shareUrl = await constructShareUrl()\r\n setShareUrlLink(shareUrl)\r\n router.push(shareUrl, { scroll: false }) // push but do not refresh\r\n setQrDialogOpen(true)\r\n }\r\n catch (error) {\r\n console.error('Failed to generate QR:', error)\r\n }\r\n finally {\r\n setIsLoadingQRCode(false)\r\n }\r\n }\r\n\r\n const handleClickScreenshot = async () => {\r\n try {\r\n let canvas: HTMLCanvasElement | null = null\r\n\r\n // Access to the map instance for map viewer\r\n if (currentViewer === ViewerNames.map && map) {\r\n // Use the map's 'render' event to capture after render completes\r\n const captureAfterRender = () => {\r\n const mapCanvas = map.getCanvas()\r\n mapCanvas.toBlob((blob) => {\r\n if (!blob) return\r\n \r\n const url = URL.createObjectURL(blob)\r\n setScreenshotBlob(blob)\r\n setScreenshotUrl(url)\r\n setScreenshotName(`screenshot-${currentViewer}-${Date.now()}`)\r\n setScreenshotDialogOpen(true)\r\n }, 'image/png', 1.0)\r\n }\r\n\r\n // Listen for the next render event\r\n map.once('render', captureAfterRender)\r\n // Trigger the repaint\r\n map.triggerRepaint()\r\n return\r\n }\r\n \r\n // For BIM viewer, force a render then capture\r\n if (currentViewer === ViewerNames.bim && world?.renderer && world?.scene && world?.camera) {\r\n // Force Three.js to render\r\n world.renderer.three.render(world.scene.three, world.camera.three)\r\n \r\n // Get the canvas and capture immediately after render\r\n canvas = document.querySelector('#bim-viewer-container canvas')\r\n if (canvas) {\r\n canvas.toBlob((blob) => {\r\n if (!blob) return\r\n \r\n const url = URL.createObjectURL(blob)\r\n setScreenshotBlob(blob)\r\n setScreenshotUrl(url)\r\n setScreenshotName(`screenshot-${currentViewer}-${Date.now()}`)\r\n setScreenshotDialogOpen(true)\r\n }, 'image/png', 1.0)\r\n }\r\n return\r\n }\r\n\r\n if (currentViewer === ViewerNames.pointcloud) {\r\n canvas = document.querySelector('#pointcloud-viewer-container canvas')\r\n } else {\r\n // Fallback to first canvas\r\n canvas = document.querySelector('canvas')\r\n }\r\n\r\n if (!canvas) {\r\n throw new Error('No canvas found')\r\n }\r\n canvas.toBlob((blob) => {\r\n if (!blob) return\r\n \r\n const url = URL.createObjectURL(blob)\r\n setScreenshotBlob(blob)\r\n setScreenshotUrl(url)\r\n setScreenshotName(`screenshot-${currentViewer}-${Date.now()}`)\r\n setScreenshotDialogOpen(true)\r\n }, 'image/png', 1.0)\r\n \r\n } catch (error) {\r\n console.error('Screenshot failed:', error)\r\n }\r\n toast.success(t('ssSuccess'))\r\n }\r\n\r\n const handleDownloadScreenshot = async () => {\r\n if (!screenshotUrl) return\r\n\r\n if (exportFormat === 'pdf') {\r\n const { default: jsPDF } = await import('jspdf')\r\n const img = new Image()\r\n img.onload = () => {\r\n // Calculate dimensions to fit on page while maintaining aspect ratio\r\n const pdf = new jsPDF({\r\n orientation: img.width > img.height ? 'landscape' : 'portrait',\r\n unit: 'px',\r\n format: [img.width, img.height]\r\n })\r\n pdf.addImage(screenshotUrl, 'PNG', 0, 0, img.width, img.height)\r\n pdf.save(`${screenshotName}.pdf`)\r\n setScreenshotDialogOpen(false)\r\n }\r\n img.src = screenshotUrl\r\n } else {\r\n // Download as PNG\r\n const a = document.createElement('a')\r\n a.href = screenshotUrl\r\n a.download = `${screenshotName}.png`\r\n a.click()\r\n setScreenshotDialogOpen(false)\r\n }\r\n }\r\n\r\n // Check if current viewer supports screenshots\r\n const canTakeScreenshot = [\r\n ViewerNames.map, \r\n ViewerNames.bim, \r\n ViewerNames.pointcloud\r\n ].includes(currentViewer as ViewerNames)\r\n\r\n return (\r\n <ToolbarSubmenu tool={tool}>\r\n <Dialog open={qrDialogOpen} onOpenChange={setQrDialogOpen}>\r\n <DropdownMenuItem onClick={(e) => { e.preventDefault() }} className=\"cursor-pointer\">\r\n <DialogTrigger asChild>\r\n <button onClick={handleShareQR} disabled={isLoadingQRCode} className=\"flex w-full gap-2 items-center\">\r\n {isLoadingQRCode ? <LoadingSpinner /> : <ScanQrCode size={16} />}\r\n <span>{isLoadingQRCode ? t('generating') : t('generate')}</span>\r\n </button>\r\n </DialogTrigger>\r\n </DropdownMenuItem>\r\n\r\n <DialogContent className=\"flex flex-col items-center gap-4 max-w-sm\">\r\n <DialogHeader>\r\n <DialogTitle>\r\n {dialogTitleKey === 'map' \r\n ? t('mapDialogTitle') \r\n : dialogTitleKey === 'bim' \r\n ? t('bimDialogTitle') \r\n : t('pointCloudDialogTitle')}\r\n </DialogTitle>\r\n </DialogHeader>\r\n <QRCodeWithLink title=\"\" urlToShare={shareUrlLink} isLoadingState={isLoadingQRCode} />\r\n </DialogContent>\r\n </Dialog>\r\n\r\n {/* Copy to clipboard */}\r\n <DropdownMenuItem onClick={handleClickShare} disabled={isLoadingShareLink} onSelect={(e) => { e.preventDefault() }} className=\"cursor-pointer\">\r\n <div className=\"flex items-center w-full gap-2\">\r\n {isLoadingShareLink ? <LoadingSpinner /> : <Link size={16} />}\r\n <span>{isLoadingShareLink ? t('copying') : t('copy')}</span>\r\n </div>\r\n </DropdownMenuItem>\r\n\r\n {/* Screenshot */}\r\n {canTakeScreenshot && (\r\n <Dialog open={screenshotDialogOpen} onOpenChange={setScreenshotDialogOpen}>\r\n <DropdownMenuItem onClick={(e) => { e.preventDefault() }} className=\"cursor-pointer\">\r\n <DialogTrigger asChild>\r\n <button onClick={handleClickScreenshot} className=\"flex w-full gap-2 items-center\">\r\n <Camera size={16}/>\r\n <span>{t('ss')}</span>\r\n </button>\r\n </DialogTrigger>\r\n </DropdownMenuItem>\r\n\r\n <DialogContent>\r\n <DialogHeader>\r\n <DialogTitle>{t('ssDownload')}</DialogTitle>\r\n </DialogHeader>\r\n \r\n <div className=\"flex flex-col gap-4\">\r\n {screenshotUrl && (\r\n <img \r\n src={screenshotUrl} \r\n alt=\"Screenshot preview\" \r\n className=\"w-full h-auto\"\r\n />\r\n )}\r\n \r\n <div className=\"flex flex-col gap-2\">\r\n <label htmlFor=\"screenshot-name\">\r\n {t('fileName')}\r\n </label>\r\n <Input\r\n id=\"screenshot-name\"\r\n value={screenshotName}\r\n onChange={(e) => setScreenshotName(e.target.value)}\r\n />\r\n </div>\r\n\r\n <div className=\"flex flex-col gap-2\">\r\n <label htmlFor=\"export-format\">\r\n {t('format')}\r\n </label>\r\n <select\r\n id=\"export-format\"\r\n value={exportFormat}\r\n onChange={(e) => setExportFormat(e.target.value as 'png' | 'pdf')}\r\n className=\"flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2\"\r\n >\r\n <option value=\"png\">{t('png')}</option>\r\n <option value=\"pdf\">{t('pdf')}</option>\r\n </select>\r\n </div>\r\n\r\n <Button onClick={handleDownloadScreenshot}>\r\n {t('download')}\r\n </Button>\r\n </div>\r\n </DialogContent>\r\n </Dialog>\r\n )}\r\n </ToolbarSubmenu>\r\n )\r\n}"],"mappings":";AAgQY,SACqB,KADrB;AA9PZ,YAAY,WAAW;AACvB,SAAS,WAAW,mBAAmB;AACvC,SAAS,uBAAuB;AAIhC,SAAS,mBAAmB;AAG5B;AAAA,EACE;AAAA,OACK;AACP;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AACP,SAAS,aAAa;AACtB,SAAS,cAAc;AACvB,SAAS,aAAa;AAGtB;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,OAEK;AAGP,OAAO,oBAAoB;AAC3B,SAAS,sBAAsB;AAC/B,SAAS,sBAAsB;AAE/B,SAAS,cAAc,YAAY,kBAAkB;AAS9C,MAAM,mBAAoD,CAAC;AAAA,EAChE;AAAA,EACA;AAAA,EACA,iBAAiB;AAAA,EACjB;AACF,MAAM;AAGJ,QAAM,IAAI,gBAAgB,WAAW;AAErC,QAAM,EAAE,OAAO,WAAW,IAAI,MAAM,WAAW,YAAY;AAC3D,QAAM,EAAE,cAAc,IAAI,WAAW;AAGrC,QAAM,EAAE,OAAO,SAAS,IAAI,MAAM,WAAW,UAAU;AACvD,QAAM,EAAE,IAAI,IAAI,SAAS;AAEzB,QAAM,EAAE,OAAO,SAAS,IAAI,MAAM,WAAW,UAAU;AACvD,QAAM,EAAE,MAAM,IAAI,SAAS;AAG3B,QAAM,SAAS,UAAU;AACzB,QAAM,WAAW,YAAY;AAG7B,QAAM,CAAC,cAAc,eAAe,IAAI,MAAM,SAAiB,EAAE;AACjE,QAAM,CAAC,QAAQ,SAAS,IAAI,MAAM,SAAiB,EAAE;AAErD,QAAM,CAAC,oBAAoB,qBAAqB,IAAI,MAAM,SAAS,KAAK;AACxE,QAAM,CAAC,iBAAiB,kBAAkB,IAAI,MAAM,SAAS,KAAK;AAElE,QAAM,CAAC,cAAc,eAAe,IAAI,MAAM,SAAS,KAAK;AAG5D,QAAM,CAAC,sBAAsB,uBAAuB,IAAI,MAAM,SAAS,KAAK;AAC5E,QAAM,CAAC,gBAAgB,iBAAiB,IAAI,MAAM,SAAsB,IAAI;AAC5E,QAAM,CAAC,eAAe,gBAAgB,IAAI,MAAM,SAAiB,EAAE;AACnE,QAAM,CAAC,gBAAgB,iBAAiB,IAAI,MAAM,SAAiB,EAAE;AACrE,QAAM,CAAC,cAAc,eAAe,IAAI,MAAM,SAAwB,KAAK;AAG3E,QAAM,UAAU,MAAM;AACpB,cAAU,OAAO,SAAS,MAAM;AAChC,oBAAgB,OAAO,SAAS,SAAS,QAAQ;AAAA,EACnD,GAAG,CAAC,QAAQ,CAAC;AAGb,QAAM,UAAU,MAAM;AACpB,QAAI,CAAC,wBAAwB,eAAe;AAC1C,UAAI,gBAAgB,aAAa;AACjC,uBAAiB,EAAE;AACnB,wBAAkB,IAAI;AAAA,IACxB;AAAA,EACF,GAAG,CAAC,sBAAsB,aAAa,CAAC;AAGxC,QAAM,mBAAmB,YAAY;AACnC,0BAAsB,IAAI;AAC1B,QAAI;AACF,YAAM,WAAW,MAAM,kBAAkB;AACzC,YAAM,mBAAmB,MAAM;AAC7B,YAAI;AACF,oBAAU,UAAU,UAAU,QAAQ;AAAA,QACxC,SACM;AAAA,QAAC;AAAA,MACT;AACA,uBAAiB;AACjB,aAAO,KAAK,UAAU,EAAE,QAAQ,MAAM,CAAC;AAAA,IACzC,SACO,OAAO;AACZ,cAAQ,MAAM,mBAAmB,KAAK;AAAA,IACxC,UACA;AACE,4BAAsB,KAAK;AAAA,IAC7B;AACA,UAAM,QAAQ,EAAE,aAAa,CAAC;AAAA,EAChC;AAGA,QAAM,gBAAgB,YAAY;AAChC,uBAAmB,IAAI;AACvB,QAAI;AACF,YAAM,WAAW,MAAM,kBAAkB;AACzC,sBAAgB,QAAQ;AACxB,aAAO,KAAK,UAAU,EAAE,QAAQ,MAAM,CAAC;AACvC,sBAAgB,IAAI;AAAA,IACtB,SACO,OAAO;AACZ,cAAQ,MAAM,0BAA0B,KAAK;AAAA,IAC/C,UACA;AACE,yBAAmB,KAAK;AAAA,IAC1B;AAAA,EACF;AAEA,QAAM,wBAAwB,YAAY;AACxC,QAAI;AACF,UAAI,SAAmC;AAGvC,UAAI,kBAAkB,YAAY,OAAO,KAAK;AAE5C,cAAM,qBAAqB,MAAM;AAC/B,gBAAM,YAAY,IAAI,UAAU;AAChC,oBAAU,OAAO,CAAC,SAAS;AACzB,gBAAI,CAAC,KAAM;AAEX,kBAAM,MAAM,IAAI,gBAAgB,IAAI;AACpC,8BAAkB,IAAI;AACtB,6BAAiB,GAAG;AACpB,8BAAkB,cAAc,aAAa,IAAI,KAAK,IAAI,CAAC,EAAE;AAC7D,oCAAwB,IAAI;AAAA,UAC9B,GAAG,aAAa,CAAG;AAAA,QACrB;AAGA,YAAI,KAAK,UAAU,kBAAkB;AAErC,YAAI,eAAe;AACnB;AAAA,MACF;AAGA,UAAI,kBAAkB,YAAY,QAAO,+BAAO,cAAY,+BAAO,WAAS,+BAAO,SAAQ;AAEzF,cAAM,SAAS,MAAM,OAAO,MAAM,MAAM,OAAO,MAAM,OAAO,KAAK;AAGjE,iBAAS,SAAS,cAAc,8BAA8B;AAC9D,YAAI,QAAQ;AACV,iBAAO,OAAO,CAAC,SAAS;AACtB,gBAAI,CAAC,KAAM;AAEX,kBAAM,MAAM,IAAI,gBAAgB,IAAI;AACpC,8BAAkB,IAAI;AACtB,6BAAiB,GAAG;AACpB,8BAAkB,cAAc,aAAa,IAAI,KAAK,IAAI,CAAC,EAAE;AAC7D,oCAAwB,IAAI;AAAA,UAC9B,GAAG,aAAa,CAAG;AAAA,QACrB;AACA;AAAA,MACF;AAEA,UAAI,kBAAkB,YAAY,YAAY;AAC5C,iBAAS,SAAS,cAAc,qCAAqC;AAAA,MACvE,OAAO;AAEL,iBAAS,SAAS,cAAc,QAAQ;AAAA,MAC1C;AAEA,UAAI,CAAC,QAAQ;AACX,cAAM,IAAI,MAAM,iBAAiB;AAAA,MACnC;AACA,aAAO,OAAO,CAAC,SAAS;AACtB,YAAI,CAAC,KAAM;AAEX,cAAM,MAAM,IAAI,gBAAgB,IAAI;AACpC,0BAAkB,IAAI;AACtB,yBAAiB,GAAG;AACpB,0BAAkB,cAAc,aAAa,IAAI,KAAK,IAAI,CAAC,EAAE;AAC7D,gCAAwB,IAAI;AAAA,MAC9B,GAAG,aAAa,CAAG;AAAA,IAErB,SAAS,OAAO;AACd,cAAQ,MAAM,sBAAsB,KAAK;AAAA,IAC3C;AACA,UAAM,QAAQ,EAAE,WAAW,CAAC;AAAA,EAC9B;AAEA,QAAM,2BAA2B,YAAY;AAC3C,QAAI,CAAC,cAAe;AAEpB,QAAI,iBAAiB,OAAO;AAC1B,YAAM,EAAE,SAAS,MAAM,IAAI,MAAM,OAAO,OAAO;AAC/C,YAAM,MAAM,IAAI,MAAM;AACtB,UAAI,SAAS,MAAM;AAEjB,cAAM,MAAM,IAAI,MAAM;AAAA,UACpB,aAAa,IAAI,QAAQ,IAAI,SAAS,cAAc;AAAA,UACpD,MAAM;AAAA,UACN,QAAQ,CAAC,IAAI,OAAO,IAAI,MAAM;AAAA,QAChC,CAAC;AACD,YAAI,SAAS,eAAe,OAAO,GAAG,GAAG,IAAI,OAAO,IAAI,MAAM;AAC9D,YAAI,KAAK,GAAG,cAAc,MAAM;AAChC,gCAAwB,KAAK;AAAA,MAC/B;AACA,UAAI,MAAM;AAAA,IACZ,OAAO;AAEL,YAAM,IAAI,SAAS,cAAc,GAAG;AACpC,QAAE,OAAO;AACT,QAAE,WAAW,GAAG,cAAc;AAC9B,QAAE,MAAM;AACR,8BAAwB,KAAK;AAAA,IAC/B;AAAA,EACF;AAGA,QAAM,oBAAoB;AAAA,IACxB,YAAY;AAAA,IACZ,YAAY;AAAA,IACZ,YAAY;AAAA,EACd,EAAE,SAAS,aAA4B;AAEvC,SACE,qBAAC,kBAAe,MACd;AAAA,yBAAC,UAAO,MAAM,cAAc,cAAc,iBACxC;AAAA,0BAAC,oBAAiB,SAAS,CAAC,MAAM;AAAE,UAAE,eAAe;AAAA,MAAE,GAAG,WAAU,kBAClE,8BAAC,iBAAc,SAAO,MACpB,+BAAC,YAAO,SAAS,eAAe,UAAU,iBAAiB,WAAU,kCAClE;AAAA,0BAAkB,oBAAC,kBAAe,IAAK,oBAAC,cAAW,MAAM,IAAI;AAAA,QAC9D,oBAAC,UAAM,4BAAkB,EAAE,YAAY,IAAI,EAAE,UAAU,GAAE;AAAA,SAC3D,GACF,GACF;AAAA,MAEA,qBAAC,iBAAc,WAAU,6CACvB;AAAA,4BAAC,gBACC,8BAAC,eACE,6BAAmB,QAChB,EAAE,gBAAgB,IAClB,mBAAmB,QACnB,EAAE,gBAAgB,IAClB,EAAE,uBAAuB,GAC/B,GACF;AAAA,QACA,oBAAC,kBAAe,OAAM,IAAG,YAAY,cAAc,gBAAgB,iBAAiB;AAAA,SACtF;AAAA,OACF;AAAA,IAGA,oBAAC,oBAAiB,SAAS,kBAAkB,UAAU,oBAAoB,UAAU,CAAC,MAAM;AAAE,QAAE,eAAe;AAAA,IAAE,GAAG,WAAU,kBAC5H,+BAAC,SAAI,WAAU,kCACZ;AAAA,2BAAqB,oBAAC,kBAAe,IAAK,oBAAC,QAAK,MAAM,IAAI;AAAA,MAC3D,oBAAC,UAAM,+BAAqB,EAAE,SAAS,IAAI,EAAE,MAAM,GAAE;AAAA,OACvD,GACF;AAAA,IAGC,qBACC,qBAAC,UAAO,MAAM,sBAAsB,cAAc,yBAChD;AAAA,0BAAC,oBAAiB,SAAS,CAAC,MAAM;AAAE,UAAE,eAAe;AAAA,MAAE,GAAG,WAAU,kBAClE,8BAAC,iBAAc,SAAO,MACpB,+BAAC,YAAO,SAAS,uBAAuB,WAAU,kCAChD;AAAA,4BAAC,UAAO,MAAM,IAAG;AAAA,QACjB,oBAAC,UAAM,YAAE,IAAI,GAAE;AAAA,SACjB,GACF,GACF;AAAA,MAEA,qBAAC,iBACC;AAAA,4BAAC,gBACC,8BAAC,eAAa,YAAE,YAAY,GAAE,GAChC;AAAA,QAEA,qBAAC,SAAI,WAAU,uBACZ;AAAA,2BACC;AAAA,YAAC;AAAA;AAAA,cACC,KAAK;AAAA,cACL,KAAI;AAAA,cACJ,WAAU;AAAA;AAAA,UACZ;AAAA,UAGF,qBAAC,SAAI,WAAU,uBACb;AAAA,gCAAC,WAAM,SAAQ,mBACZ,YAAE,UAAU,GACf;AAAA,YACA;AAAA,cAAC;AAAA;AAAA,gBACC,IAAG;AAAA,gBACH,OAAO;AAAA,gBACP,UAAU,CAAC,MAAM,kBAAkB,EAAE,OAAO,KAAK;AAAA;AAAA,YACnD;AAAA,aACF;AAAA,UAEA,qBAAC,SAAI,WAAU,uBACb;AAAA,gCAAC,WAAM,SAAQ,iBACZ,YAAE,QAAQ,GACb;AAAA,YACA;AAAA,cAAC;AAAA;AAAA,gBACC,IAAG;AAAA,gBACH,OAAO;AAAA,gBACP,UAAU,CAAC,MAAM,gBAAgB,EAAE,OAAO,KAAsB;AAAA,gBAChE,WAAU;AAAA,gBAEV;AAAA,sCAAC,YAAO,OAAM,OAAO,YAAE,KAAK,GAAE;AAAA,kBAC9B,oBAAC,YAAO,OAAM,OAAO,YAAE,KAAK,GAAE;AAAA;AAAA;AAAA,YAChC;AAAA,aACF;AAAA,UAEA,oBAAC,UAAO,SAAS,0BACd,YAAE,UAAU,GACf;AAAA,WACF;AAAA,SACF;AAAA,OACF;AAAA,KAEJ;AAEJ;","names":[]}
|
|
@@ -14,8 +14,8 @@ function GeocoderInput({ onSelect }) {
|
|
|
14
14
|
buildingLongitude: coordinates[0],
|
|
15
15
|
buildingLatitude: coordinates[1],
|
|
16
16
|
buildingStreetName: properties == null ? void 0 : properties.street,
|
|
17
|
-
buildingCountrySubdivision: properties == null ? void 0 : properties.region_a,
|
|
18
|
-
buildingMunicipality: properties == null ? void 0 : properties.locality,
|
|
17
|
+
buildingCountrySubdivision: (properties == null ? void 0 : properties.region_a) || (properties == null ? void 0 : properties.region),
|
|
18
|
+
buildingMunicipality: (properties == null ? void 0 : properties.locality) || (properties == null ? void 0 : properties.county),
|
|
19
19
|
buildingPostalCode: properties == null ? void 0 : properties.postalcode
|
|
20
20
|
});
|
|
21
21
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../src/core/components/viewers/Data/buildingDetails/GeocoderInput.tsx"],"sourcesContent":["import React from 'react'\r\nimport Geocoder from '../../map/src/Geocoder'\r\n\r\ninterface GeocoderInputProps {\r\n onSelect: (fields: {\r\n buildingAddress: string\r\n buildingLongitude: number\r\n buildingLatitude: number\r\n buildingStreetName?: string\r\n buildingCountrySubdivision?: string\r\n buildingMunicipality?: string\r\n buildingPostalCode?: string\r\n }) => void\r\n}\r\n\r\nexport default function GeocoderInput({ onSelect }: GeocoderInputProps) {\r\n return (\r\n <Geocoder\r\n showDatabaseBuildings={false}\r\n alwaysExpanded={true}\r\n onSelect={(addressData: any) => {\r\n if (!addressData) return\r\n const { formatted_address, coordinates, properties } = addressData\r\n onSelect({\r\n buildingAddress: formatted_address,\r\n buildingLongitude: coordinates[0],\r\n buildingLatitude: coordinates[1],\r\n buildingStreetName: properties?.street,\r\n buildingCountrySubdivision: properties?.region_a,\r\n buildingMunicipality: properties?.locality,\r\n buildingPostalCode: properties?.postalcode,\r\n })\r\n }}\r\n />\r\n )\r\n}\r\n"],"mappings":"AAiBI;AAhBJ,OAAO,cAAc;AAcN,SAAR,cAA+B,EAAE,SAAS,GAAuB;AACtE,SACE;AAAA,IAAC;AAAA;AAAA,MACC,uBAAuB;AAAA,MACvB,gBAAgB;AAAA,MAChB,UAAU,CAAC,gBAAqB;AAC9B,YAAI,CAAC,YAAa;AAClB,cAAM,EAAE,mBAAmB,aAAa,WAAW,IAAI;AACvD,iBAAS;AAAA,UACP,iBAAiB;AAAA,UACjB,mBAAmB,YAAY,CAAC;AAAA,UAChC,kBAAkB,YAAY,CAAC;AAAA,UAC/B,oBAAoB,yCAAY;AAAA,UAChC,
|
|
1
|
+
{"version":3,"sources":["../../../../../../src/core/components/viewers/Data/buildingDetails/GeocoderInput.tsx"],"sourcesContent":["import React from 'react'\r\nimport Geocoder from '../../map/src/Geocoder'\r\n\r\ninterface GeocoderInputProps {\r\n onSelect: (fields: {\r\n buildingAddress: string\r\n buildingLongitude: number\r\n buildingLatitude: number\r\n buildingStreetName?: string\r\n buildingCountrySubdivision?: string\r\n buildingMunicipality?: string\r\n buildingPostalCode?: string\r\n }) => void\r\n}\r\n\r\nexport default function GeocoderInput({ onSelect }: GeocoderInputProps) {\r\n return (\r\n <Geocoder\r\n showDatabaseBuildings={false}\r\n alwaysExpanded={true}\r\n onSelect={(addressData: any) => {\r\n if (!addressData) return\r\n const { formatted_address, coordinates, properties } = addressData\r\n onSelect({\r\n buildingAddress: formatted_address,\r\n buildingLongitude: coordinates[0],\r\n buildingLatitude: coordinates[1],\r\n buildingStreetName: properties?.street,\r\n buildingCountrySubdivision: properties?.region_a || properties?.region,\r\n buildingMunicipality: properties?.locality || properties?.county,\r\n buildingPostalCode: properties?.postalcode,\r\n })\r\n }}\r\n />\r\n )\r\n}\r\n"],"mappings":"AAiBI;AAhBJ,OAAO,cAAc;AAcN,SAAR,cAA+B,EAAE,SAAS,GAAuB;AACtE,SACE;AAAA,IAAC;AAAA;AAAA,MACC,uBAAuB;AAAA,MACvB,gBAAgB;AAAA,MAChB,UAAU,CAAC,gBAAqB;AAC9B,YAAI,CAAC,YAAa;AAClB,cAAM,EAAE,mBAAmB,aAAa,WAAW,IAAI;AACvD,iBAAS;AAAA,UACP,iBAAiB;AAAA,UACjB,mBAAmB,YAAY,CAAC;AAAA,UAChC,kBAAkB,YAAY,CAAC;AAAA,UAC/B,oBAAoB,yCAAY;AAAA,UAChC,6BAA4B,yCAAY,cAAY,yCAAY;AAAA,UAChE,uBAAsB,yCAAY,cAAY,yCAAY;AAAA,UAC1D,oBAAoB,yCAAY;AAAA,QAClC,CAAC;AAAA,MACH;AAAA;AAAA,EACF;AAEJ;","names":[]}
|
|
@@ -181,8 +181,8 @@ const AssociatedBuildingsTable = ({
|
|
|
181
181
|
buildingLongitude: coordinates[0],
|
|
182
182
|
buildingLatitude: coordinates[1],
|
|
183
183
|
buildingStreetName: properties.street,
|
|
184
|
-
buildingCountrySubdivision: properties.region_a,
|
|
185
|
-
buildingMunicipality: properties.locality,
|
|
184
|
+
buildingCountrySubdivision: properties.region_a || properties.region,
|
|
185
|
+
buildingMunicipality: properties.locality || properties.county,
|
|
186
186
|
buildingPostalCode: properties.postalcode
|
|
187
187
|
};
|
|
188
188
|
const result = await createBuilding({
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../src/core/components/viewers/Data/siteDetails/AssociatedBuildings.tsx"],"sourcesContent":["\"use client\"\r\n\r\n// Dependencies\r\nimport * as React from 'react'\r\nimport type { Building } from '../../../../types/dbTypes'\r\nimport { useSession } from 'next-auth/react'\r\nimport { toast } from 'sonner'\r\n\r\n// Utils\r\nimport { useAssociatedBuildingsColumns } from '../utils/Columns'\r\nimport { filterHelper } from '../utils/filterHelper'\r\nimport { fetchSuggestions, parseLocation } from '../../map/utils/geocoder'\r\nimport { useMapContext } from '../../../../store'\r\n\r\n// Shadcn Components\r\nimport { DataTable, Input, Button, LoadingSpinner } from '../../../../components/ui/'\r\nimport {\r\n DropdownMenu,\r\n DropdownMenuContent,\r\n DropdownMenuItem,\r\n DropdownMenuTrigger,\r\n DropdownMenuSeparator,\r\n DropdownMenuLabel,\r\n} from '../../../../components/ui/DropdownMenu'\r\n\r\n// Custom Components\r\nimport FiltersDialog from '../advancedFilter/FiltersDialog'\r\n\r\n// Icons\r\nimport * as LR from 'lucide-react'\r\nimport { useTranslations } from 'next-intl'\r\nimport { useBuildings, useCreateBuilding } from '../../../../hooks/buildings/buildings'\r\nimport { useUser } from '../../../../hooks/users/users'\r\n\r\n\r\ninterface AssociatedBuildingsTableProps {\r\n buildings: Building[]\r\n onRowClick?: (building: Building) => void\r\n siteId?: number\r\n onAttachBuilding?: (building: Building) => void\r\n editing?: boolean\r\n setEditing?: (editing: boolean) => void\r\n}\r\n\r\nconst AssociatedBuildingsTable: React.FC<AssociatedBuildingsTableProps> = ({\r\n buildings,\r\n onRowClick,\r\n siteId,\r\n onAttachBuilding,\r\n editing,\r\n setEditing,\r\n}) => {\r\n // Translations\r\n const t = useTranslations('AssociatedBuildings')\r\n\r\n const [searchTerm, setSearchTerm] = React.useState('')\r\n const [attachSearchTerm, setAttachSearchTerm] = React.useState('')\r\n const [filters, setFilters] = React.useState([\r\n {\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 ])\r\n const [activeFilters, setActiveFilters] = React.useState([])\r\n // Track newly attached buildings for preview\r\n const [previewBuildings, setPreviewBuildings] = React.useState<Building[]>([])\r\n \r\n const [isCreatingBuilding, setIsCreatingBuilding] = React.useState(false)\r\n \r\n const [geocodedSuggestions, setGeocodedSuggestions] = React.useState([])\r\n const [isLoadingGeocoded, setIsLoadingGeocoded] = React.useState(false)\r\n\r\n const { state: mapState } = useMapContext()\r\n const { organizationCountry } = mapState.map\r\n\r\n // Fetch all available buildings\r\n const { buildings: allBuildings, isLoading } = useBuildings()\r\n \r\n const { createBuilding } = useCreateBuilding()\r\n \r\n // Session and user data\r\n const { data: session, status } = useSession()\r\n const { user, isError, updateUser } = useUser(session?.user?.id || '')\r\n\r\n // Combine original buildings with preview buildings\r\n const displayBuildings = React.useMemo(() => {\r\n // Create a unique set of buildings based on ID\r\n const combinedBuildings = [...buildings]\r\n\r\n // Only add preview buildings that aren't already in the original buildings array\r\n for (const previewBuilding of previewBuildings) {\r\n if (!combinedBuildings.some(b => b.id === previewBuilding.id)) {\r\n combinedBuildings.push({\r\n ...previewBuilding,\r\n })\r\n }\r\n }\r\n\r\n return combinedBuildings\r\n }, [buildings, previewBuildings])\r\n\r\n // Apply filters to the buildings data\r\n const filteredBuildings = React.useMemo(() => {\r\n // First apply search filter\r\n let filtered = displayBuildings.filter(building =>\r\n [\r\n building.buildingName,\r\n building.buildingAddress,\r\n building.buildingCountrySubdivision,\r\n building.buildingMunicipality,\r\n building.buildingProjectType,\r\n ].some(value =>\r\n String(value || '').toLowerCase().includes(searchTerm.toLowerCase()),\r\n ),\r\n )\r\n\r\n // Then apply advanced filters\r\n if (activeFilters && activeFilters.length > 0) {\r\n filtered = filterHelper(filtered, activeFilters)\r\n }\r\n\r\n return filtered\r\n }, [displayBuildings, searchTerm, activeFilters])\r\n\r\n // Filter buildings for attachment dropdown\r\n const filteredAttachableBuildings = React.useMemo(() => {\r\n if (!allBuildings) return []\r\n\r\n // Filter out buildings that are already attached to this site\r\n // or are in the preview list\r\n const existingBuildingIds = new Set([...buildings, ...previewBuildings].map(b => b.id))\r\n const availableBuildings = allBuildings.filter(b => !existingBuildingIds.has(b.id))\r\n\r\n // Return empty array when search term is empty\r\n if (!attachSearchTerm.trim()) return []\r\n\r\n return availableBuildings\r\n .filter(building =>\r\n [\r\n building.buildingName,\r\n building.buildingAddress,\r\n ].some(value =>\r\n String(value || '').toLowerCase().includes(attachSearchTerm.toLowerCase()),\r\n ),\r\n )\r\n .sort((a, b) => {\r\n // Sort by relevance (name > address)\r\n const aNameMatch = a.buildingName?.toLowerCase().includes(attachSearchTerm.toLowerCase())\r\n const bNameMatch = b.buildingName?.toLowerCase().includes(attachSearchTerm.toLowerCase())\r\n\r\n if (aNameMatch && !bNameMatch) return -1\r\n if (!aNameMatch && bNameMatch) return 1\r\n\r\n return 0\r\n })\r\n .slice(0, 3) // Limit to 3 results\r\n }, [allBuildings, attachSearchTerm, buildings, previewBuildings])\r\n\r\n // Filter geocoded suggestions to exclude addresses that match existing buildings\r\n const filteredGeocodedSuggestions = React.useMemo(() => {\r\n if (!geocodedSuggestions.length || !allBuildings) return geocodedSuggestions\r\n\r\n // Get all existing building addresses\r\n const existingAddresses = new Set(\r\n allBuildings\r\n .map(b => b.buildingAddress?.toLowerCase().trim())\r\n .filter(Boolean)\r\n )\r\n\r\n // Filter out geocoded results that match existing building addresses\r\n return geocodedSuggestions.filter((feature) => {\r\n const geocodedAddress = feature.properties?.label?.toLowerCase().trim()\r\n return geocodedAddress && !existingAddresses.has(geocodedAddress)\r\n }).slice(0, 3) // Limit to 3 results\r\n }, [geocodedSuggestions, allBuildings])\r\n\r\n // Debounced fetch for geocoded addresses\r\n React.useEffect(() => {\r\n let timeoutId: NodeJS.Timeout\r\n\r\n const fetchGeocoded = async () => {\r\n if (!attachSearchTerm.trim() || attachSearchTerm.length < 3) {\r\n setGeocodedSuggestions([])\r\n setIsLoadingGeocoded(false)\r\n return\r\n }\r\n\r\n setIsLoadingGeocoded(true)\r\n try {\r\n const results = await fetchSuggestions(attachSearchTerm, organizationCountry)\r\n setGeocodedSuggestions(results || [])\r\n } catch (error) {\r\n console.error('Error fetching geocoded suggestions:', error)\r\n setGeocodedSuggestions([])\r\n } finally {\r\n setIsLoadingGeocoded(false)\r\n }\r\n }\r\n\r\n timeoutId = setTimeout(fetchGeocoded, 300)\r\n\r\n return () => clearTimeout(timeoutId)\r\n }, [attachSearchTerm])\r\n\r\n const handleApplyFilters = (appliedFilters) => {\r\n setActiveFilters(appliedFilters)\r\n }\r\n\r\n const handleAttachBuilding = async (building: Building) => {\r\n // Enable editing mode if setEditing is provided\r\n if (setEditing) {\r\n setEditing(true)\r\n }\r\n // Add to preview list for immediate feedback\r\n setPreviewBuildings(prev => [...prev, building])\r\n // Reset search field\r\n setAttachSearchTerm('')\r\n // Call the onAttachBuilding prop to update parent state\r\n if (onAttachBuilding) {\r\n onAttachBuilding(building)\r\n }\r\n }\r\n\r\n const handleCreateAndAttach = async (feature: any) => {\r\n if (!user?.organizationId) {\r\n toast.error('Organization not found')\r\n return\r\n }\r\n\r\n setIsCreatingBuilding(true)\r\n try {\r\n // Parse the feature data\r\n const coordinates = feature.geometry?.coordinates || []\r\n const properties = feature.properties || {}\r\n \r\n const buildingData = {\r\n buildingAddress: properties.label || attachSearchTerm,\r\n buildingLongitude: coordinates[0],\r\n buildingLatitude: coordinates[1],\r\n buildingStreetName: properties.street,\r\n buildingCountrySubdivision: properties.region_a,\r\n buildingMunicipality: properties.locality,\r\n buildingPostalCode: properties.postalcode,\r\n }\r\n \r\n // Create the building\r\n const result = await createBuilding({\r\n buildingData,\r\n organizationId: user?.organizationId ? String(user.organizationId) : null,\r\n })\r\n // Auto-attach the newly created building\r\n if (result) {\r\n if (setEditing) {\r\n setEditing(true)\r\n }\r\n setPreviewBuildings(prev => {\r\n const updated = [...prev, result]\r\n return updated\r\n })\r\n if (onAttachBuilding) {\r\n onAttachBuilding(result)\r\n }\r\n toast.success(t('createAttachToastSuccess'))\r\n }\r\n setAttachSearchTerm('')\r\n setGeocodedSuggestions([])\r\n } catch (error) {\r\n toast.error(t('createAttachToastError'))\r\n } finally {\r\n setIsCreatingBuilding(false)\r\n }\r\n} \r\n\r\n return (\r\n <div className=\"flex flex-col\">\r\n <div className=\"flex flex-row justify-between\">\r\n {/* search & filters */}\r\n <div className=\"flex flex-row gap-12 sm:gap-2 justify-between sm:justify-start w-full\">\r\n <div className=\"pb-4\">\r\n <Input\r\n placeholder={t('searchPlaceholder1')}\r\n value={searchTerm}\r\n onChange={e => setSearchTerm(e.target.value)}\r\n />\r\n </div>\r\n <FiltersDialog\r\n data={displayBuildings}\r\n isBuilding={true}\r\n filters={filters}\r\n activeFilters={activeFilters}\r\n setFilters={setFilters}\r\n setActiveFilters={setActiveFilters}\r\n onApplyFilters={handleApplyFilters}\r\n />\r\n </div>\r\n\r\n {/* attach building dropdown */}\r\n <DropdownMenu>\r\n <DropdownMenuTrigger asChild>\r\n <Button variant=\"secondary\">{t('attachTrigger')}</Button>\r\n </DropdownMenuTrigger>\r\n <DropdownMenuContent align=\"end\" className=\"w-[300px]\">\r\n <div className=\"relative\">\r\n <LR.Search className=\"absolute left-3 top-2.5 h-4 w-4 text-muted-foreground\" />\r\n <Input\r\n placeholder={t('searchPlaceholder2')}\r\n className=\"pl-8\"\r\n value={attachSearchTerm}\r\n autoFocus\r\n onChange={e => setAttachSearchTerm(e.target.value)}\r\n onKeyDown={e => e.stopPropagation()}\r\n />\r\n </div>\r\n\r\n {isLoading || isLoadingGeocoded ? (\r\n <DropdownMenuItem disabled>\r\n <LoadingSpinner />\r\n </DropdownMenuItem>\r\n ) : attachSearchTerm.trim() === '' ? (\r\n <DropdownMenuItem disabled>\r\n Type to search for buildings\r\n </DropdownMenuItem>\r\n ) : (\r\n <>\r\n {/* Existing Buildings Section */}\r\n {filteredAttachableBuildings.length > 0 && (\r\n <>\r\n <DropdownMenuLabel>Existing Buildings</DropdownMenuLabel>\r\n {filteredAttachableBuildings.map(building => (\r\n <DropdownMenuItem\r\n key={building.id}\r\n onClick={() => handleAttachBuilding(building)}\r\n >\r\n <div className=\"flex flex-col\">\r\n <span className=\"flex items-center\">\r\n {building.buildingName || 'Unnamed Building'}\r\n </span>\r\n {building.buildingAddress && (\r\n <span className=\"text-muted-foreground text-sm\">\r\n {building.buildingAddress}\r\n </span>\r\n )}\r\n </div>\r\n </DropdownMenuItem>\r\n ))}\r\n </>\r\n )}\r\n\r\n {/* Separator if both sections have content */}\r\n {filteredAttachableBuildings.length > 0 && filteredGeocodedSuggestions.length > 0 && (\r\n <DropdownMenuSeparator />\r\n )}\r\n\r\n {/* Geocoded Addresses Section */}\r\n {filteredGeocodedSuggestions.length > 0 && (\r\n <>\r\n <DropdownMenuLabel>Create New Building</DropdownMenuLabel>\r\n {filteredGeocodedSuggestions.map((feature) => {\r\n const location = parseLocation(feature)\r\n const uniqueKey = feature.properties?.gid\r\n || `${feature.properties?.name}-${feature.geometry?.coordinates?.[0]}-${feature.geometry?.coordinates?.[1]}`\r\n\r\n return (\r\n <DropdownMenuItem\r\n key={uniqueKey}\r\n onClick={() => handleCreateAndAttach(feature)}\r\n disabled={isCreatingBuilding}\r\n >\r\n <div className=\"flex items-center gap-2 w-full\">\r\n <LR.Plus className=\"h-4 w-4 flex-shrink-0\" />\r\n <div className=\"flex flex-col flex-1 min-w-0\">\r\n <span className=\"font-medium truncate\">{location.name}</span>\r\n <span className=\"text-muted-foreground text-sm truncate\">\r\n {location.region}\r\n </span>\r\n </div>\r\n </div>\r\n </DropdownMenuItem>\r\n )\r\n })}\r\n </>\r\n )}\r\n\r\n {/* No results at all */}\r\n {filteredAttachableBuildings.length === 0 && filteredGeocodedSuggestions.length === 0 && (\r\n <DropdownMenuItem disabled>\r\n No buildings or addresses found\r\n </DropdownMenuItem>\r\n )}\r\n </>\r\n )}\r\n </DropdownMenuContent>\r\n </DropdownMenu>\r\n </div>\r\n\r\n {previewBuildings.length > 0 && editing && (\r\n <div className=\"mb-4 p-3 bg-muted rounded-md border border-border\">\r\n <p className=\"text-sm text-foreground\">\r\n <strong>{previewBuildings.length}</strong>\r\n {' '}\r\n {t('msg1')}\r\n {previewBuildings.length > 1 ? 's' : ''}\r\n {' '}\r\n {t('msg2')}\r\n </p>\r\n </div>\r\n )}\r\n\r\n <div className=\"py-4\">\r\n <DataTable\r\n columns={useAssociatedBuildingsColumns()}\r\n data={filteredBuildings || []}\r\n onRowClick={onRowClick}\r\n className=\"\"\r\n showPagination={true}\r\n />\r\n </div>\r\n </div>\r\n )\r\n}\r\n\r\nexport default AssociatedBuildingsTable"],"mappings":"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","names":["_a"]}
|
|
1
|
+
{"version":3,"sources":["../../../../../../src/core/components/viewers/Data/siteDetails/AssociatedBuildings.tsx"],"sourcesContent":["\"use client\"\r\n\r\n// Dependencies\r\nimport * as React from 'react'\r\nimport type { Building } from '../../../../types/dbTypes'\r\nimport { useSession } from 'next-auth/react'\r\nimport { toast } from 'sonner'\r\n\r\n// Utils\r\nimport { useAssociatedBuildingsColumns } from '../utils/Columns'\r\nimport { filterHelper } from '../utils/filterHelper'\r\nimport { fetchSuggestions, parseLocation } from '../../map/utils/geocoder'\r\nimport { useMapContext } from '../../../../store'\r\n\r\n// Shadcn Components\r\nimport { DataTable, Input, Button, LoadingSpinner } from '../../../../components/ui/'\r\nimport {\r\n DropdownMenu,\r\n DropdownMenuContent,\r\n DropdownMenuItem,\r\n DropdownMenuTrigger,\r\n DropdownMenuSeparator,\r\n DropdownMenuLabel,\r\n} from '../../../../components/ui/DropdownMenu'\r\n\r\n// Custom Components\r\nimport FiltersDialog from '../advancedFilter/FiltersDialog'\r\n\r\n// Icons\r\nimport * as LR from 'lucide-react'\r\nimport { useTranslations } from 'next-intl'\r\nimport { useBuildings, useCreateBuilding } from '../../../../hooks/buildings/buildings'\r\nimport { useUser } from '../../../../hooks/users/users'\r\n\r\n\r\ninterface AssociatedBuildingsTableProps {\r\n buildings: Building[]\r\n onRowClick?: (building: Building) => void\r\n siteId?: number\r\n onAttachBuilding?: (building: Building) => void\r\n editing?: boolean\r\n setEditing?: (editing: boolean) => void\r\n}\r\n\r\nconst AssociatedBuildingsTable: React.FC<AssociatedBuildingsTableProps> = ({\r\n buildings,\r\n onRowClick,\r\n siteId,\r\n onAttachBuilding,\r\n editing,\r\n setEditing,\r\n}) => {\r\n // Translations\r\n const t = useTranslations('AssociatedBuildings')\r\n\r\n const [searchTerm, setSearchTerm] = React.useState('')\r\n const [attachSearchTerm, setAttachSearchTerm] = React.useState('')\r\n const [filters, setFilters] = React.useState([\r\n {\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 ])\r\n const [activeFilters, setActiveFilters] = React.useState([])\r\n // Track newly attached buildings for preview\r\n const [previewBuildings, setPreviewBuildings] = React.useState<Building[]>([])\r\n \r\n const [isCreatingBuilding, setIsCreatingBuilding] = React.useState(false)\r\n \r\n const [geocodedSuggestions, setGeocodedSuggestions] = React.useState([])\r\n const [isLoadingGeocoded, setIsLoadingGeocoded] = React.useState(false)\r\n\r\n const { state: mapState } = useMapContext()\r\n const { organizationCountry } = mapState.map\r\n\r\n // Fetch all available buildings\r\n const { buildings: allBuildings, isLoading } = useBuildings()\r\n \r\n const { createBuilding } = useCreateBuilding()\r\n \r\n // Session and user data\r\n const { data: session, status } = useSession()\r\n const { user, isError, updateUser } = useUser(session?.user?.id || '')\r\n\r\n // Combine original buildings with preview buildings\r\n const displayBuildings = React.useMemo(() => {\r\n // Create a unique set of buildings based on ID\r\n const combinedBuildings = [...buildings]\r\n\r\n // Only add preview buildings that aren't already in the original buildings array\r\n for (const previewBuilding of previewBuildings) {\r\n if (!combinedBuildings.some(b => b.id === previewBuilding.id)) {\r\n combinedBuildings.push({\r\n ...previewBuilding,\r\n })\r\n }\r\n }\r\n\r\n return combinedBuildings\r\n }, [buildings, previewBuildings])\r\n\r\n // Apply filters to the buildings data\r\n const filteredBuildings = React.useMemo(() => {\r\n // First apply search filter\r\n let filtered = displayBuildings.filter(building =>\r\n [\r\n building.buildingName,\r\n building.buildingAddress,\r\n building.buildingCountrySubdivision,\r\n building.buildingMunicipality,\r\n building.buildingProjectType,\r\n ].some(value =>\r\n String(value || '').toLowerCase().includes(searchTerm.toLowerCase()),\r\n ),\r\n )\r\n\r\n // Then apply advanced filters\r\n if (activeFilters && activeFilters.length > 0) {\r\n filtered = filterHelper(filtered, activeFilters)\r\n }\r\n\r\n return filtered\r\n }, [displayBuildings, searchTerm, activeFilters])\r\n\r\n // Filter buildings for attachment dropdown\r\n const filteredAttachableBuildings = React.useMemo(() => {\r\n if (!allBuildings) return []\r\n\r\n // Filter out buildings that are already attached to this site\r\n // or are in the preview list\r\n const existingBuildingIds = new Set([...buildings, ...previewBuildings].map(b => b.id))\r\n const availableBuildings = allBuildings.filter(b => !existingBuildingIds.has(b.id))\r\n\r\n // Return empty array when search term is empty\r\n if (!attachSearchTerm.trim()) return []\r\n\r\n return availableBuildings\r\n .filter(building =>\r\n [\r\n building.buildingName,\r\n building.buildingAddress,\r\n ].some(value =>\r\n String(value || '').toLowerCase().includes(attachSearchTerm.toLowerCase()),\r\n ),\r\n )\r\n .sort((a, b) => {\r\n // Sort by relevance (name > address)\r\n const aNameMatch = a.buildingName?.toLowerCase().includes(attachSearchTerm.toLowerCase())\r\n const bNameMatch = b.buildingName?.toLowerCase().includes(attachSearchTerm.toLowerCase())\r\n\r\n if (aNameMatch && !bNameMatch) return -1\r\n if (!aNameMatch && bNameMatch) return 1\r\n\r\n return 0\r\n })\r\n .slice(0, 3) // Limit to 3 results\r\n }, [allBuildings, attachSearchTerm, buildings, previewBuildings])\r\n\r\n // Filter geocoded suggestions to exclude addresses that match existing buildings\r\n const filteredGeocodedSuggestions = React.useMemo(() => {\r\n if (!geocodedSuggestions.length || !allBuildings) return geocodedSuggestions\r\n\r\n // Get all existing building addresses\r\n const existingAddresses = new Set(\r\n allBuildings\r\n .map(b => b.buildingAddress?.toLowerCase().trim())\r\n .filter(Boolean)\r\n )\r\n\r\n // Filter out geocoded results that match existing building addresses\r\n return geocodedSuggestions.filter((feature) => {\r\n const geocodedAddress = feature.properties?.label?.toLowerCase().trim()\r\n return geocodedAddress && !existingAddresses.has(geocodedAddress)\r\n }).slice(0, 3) // Limit to 3 results\r\n }, [geocodedSuggestions, allBuildings])\r\n\r\n // Debounced fetch for geocoded addresses\r\n React.useEffect(() => {\r\n let timeoutId: NodeJS.Timeout\r\n\r\n const fetchGeocoded = async () => {\r\n if (!attachSearchTerm.trim() || attachSearchTerm.length < 3) {\r\n setGeocodedSuggestions([])\r\n setIsLoadingGeocoded(false)\r\n return\r\n }\r\n\r\n setIsLoadingGeocoded(true)\r\n try {\r\n const results = await fetchSuggestions(attachSearchTerm, organizationCountry)\r\n setGeocodedSuggestions(results || [])\r\n } catch (error) {\r\n console.error('Error fetching geocoded suggestions:', error)\r\n setGeocodedSuggestions([])\r\n } finally {\r\n setIsLoadingGeocoded(false)\r\n }\r\n }\r\n\r\n timeoutId = setTimeout(fetchGeocoded, 300)\r\n\r\n return () => clearTimeout(timeoutId)\r\n }, [attachSearchTerm])\r\n\r\n const handleApplyFilters = (appliedFilters) => {\r\n setActiveFilters(appliedFilters)\r\n }\r\n\r\n const handleAttachBuilding = async (building: Building) => {\r\n // Enable editing mode if setEditing is provided\r\n if (setEditing) {\r\n setEditing(true)\r\n }\r\n // Add to preview list for immediate feedback\r\n setPreviewBuildings(prev => [...prev, building])\r\n // Reset search field\r\n setAttachSearchTerm('')\r\n // Call the onAttachBuilding prop to update parent state\r\n if (onAttachBuilding) {\r\n onAttachBuilding(building)\r\n }\r\n }\r\n\r\n const handleCreateAndAttach = async (feature: any) => {\r\n if (!user?.organizationId) {\r\n toast.error('Organization not found')\r\n return\r\n }\r\n\r\n setIsCreatingBuilding(true)\r\n try {\r\n // Parse the feature data\r\n const coordinates = feature.geometry?.coordinates || []\r\n const properties = feature.properties || {}\r\n \r\n const buildingData = {\r\n buildingAddress: properties.label || attachSearchTerm,\r\n buildingLongitude: coordinates[0],\r\n buildingLatitude: coordinates[1],\r\n buildingStreetName: properties.street,\r\n buildingCountrySubdivision: properties.region_a || properties.region,\r\n buildingMunicipality: properties.locality || properties.county,\r\n buildingPostalCode: properties.postalcode,\r\n }\r\n \r\n // Create the building\r\n const result = await createBuilding({\r\n buildingData,\r\n organizationId: user?.organizationId ? String(user.organizationId) : null,\r\n })\r\n // Auto-attach the newly created building\r\n if (result) {\r\n if (setEditing) {\r\n setEditing(true)\r\n }\r\n setPreviewBuildings(prev => {\r\n const updated = [...prev, result]\r\n return updated\r\n })\r\n if (onAttachBuilding) {\r\n onAttachBuilding(result)\r\n }\r\n toast.success(t('createAttachToastSuccess'))\r\n }\r\n setAttachSearchTerm('')\r\n setGeocodedSuggestions([])\r\n } catch (error) {\r\n toast.error(t('createAttachToastError'))\r\n } finally {\r\n setIsCreatingBuilding(false)\r\n }\r\n} \r\n\r\n return (\r\n <div className=\"flex flex-col\">\r\n <div className=\"flex flex-row justify-between\">\r\n {/* search & filters */}\r\n <div className=\"flex flex-row gap-12 sm:gap-2 justify-between sm:justify-start w-full\">\r\n <div className=\"pb-4\">\r\n <Input\r\n placeholder={t('searchPlaceholder1')}\r\n value={searchTerm}\r\n onChange={e => setSearchTerm(e.target.value)}\r\n />\r\n </div>\r\n <FiltersDialog\r\n data={displayBuildings}\r\n isBuilding={true}\r\n filters={filters}\r\n activeFilters={activeFilters}\r\n setFilters={setFilters}\r\n setActiveFilters={setActiveFilters}\r\n onApplyFilters={handleApplyFilters}\r\n />\r\n </div>\r\n\r\n {/* attach building dropdown */}\r\n <DropdownMenu>\r\n <DropdownMenuTrigger asChild>\r\n <Button variant=\"secondary\">{t('attachTrigger')}</Button>\r\n </DropdownMenuTrigger>\r\n <DropdownMenuContent align=\"end\" className=\"w-[300px]\">\r\n <div className=\"relative\">\r\n <LR.Search className=\"absolute left-3 top-2.5 h-4 w-4 text-muted-foreground\" />\r\n <Input\r\n placeholder={t('searchPlaceholder2')}\r\n className=\"pl-8\"\r\n value={attachSearchTerm}\r\n autoFocus\r\n onChange={e => setAttachSearchTerm(e.target.value)}\r\n onKeyDown={e => e.stopPropagation()}\r\n />\r\n </div>\r\n\r\n {isLoading || isLoadingGeocoded ? (\r\n <DropdownMenuItem disabled>\r\n <LoadingSpinner />\r\n </DropdownMenuItem>\r\n ) : attachSearchTerm.trim() === '' ? (\r\n <DropdownMenuItem disabled>\r\n Type to search for buildings\r\n </DropdownMenuItem>\r\n ) : (\r\n <>\r\n {/* Existing Buildings Section */}\r\n {filteredAttachableBuildings.length > 0 && (\r\n <>\r\n <DropdownMenuLabel>Existing Buildings</DropdownMenuLabel>\r\n {filteredAttachableBuildings.map(building => (\r\n <DropdownMenuItem\r\n key={building.id}\r\n onClick={() => handleAttachBuilding(building)}\r\n >\r\n <div className=\"flex flex-col\">\r\n <span className=\"flex items-center\">\r\n {building.buildingName || 'Unnamed Building'}\r\n </span>\r\n {building.buildingAddress && (\r\n <span className=\"text-muted-foreground text-sm\">\r\n {building.buildingAddress}\r\n </span>\r\n )}\r\n </div>\r\n </DropdownMenuItem>\r\n ))}\r\n </>\r\n )}\r\n\r\n {/* Separator if both sections have content */}\r\n {filteredAttachableBuildings.length > 0 && filteredGeocodedSuggestions.length > 0 && (\r\n <DropdownMenuSeparator />\r\n )}\r\n\r\n {/* Geocoded Addresses Section */}\r\n {filteredGeocodedSuggestions.length > 0 && (\r\n <>\r\n <DropdownMenuLabel>Create New Building</DropdownMenuLabel>\r\n {filteredGeocodedSuggestions.map((feature) => {\r\n const location = parseLocation(feature)\r\n const uniqueKey = feature.properties?.gid\r\n || `${feature.properties?.name}-${feature.geometry?.coordinates?.[0]}-${feature.geometry?.coordinates?.[1]}`\r\n\r\n return (\r\n <DropdownMenuItem\r\n key={uniqueKey}\r\n onClick={() => handleCreateAndAttach(feature)}\r\n disabled={isCreatingBuilding}\r\n >\r\n <div className=\"flex items-center gap-2 w-full\">\r\n <LR.Plus className=\"h-4 w-4 flex-shrink-0\" />\r\n <div className=\"flex flex-col flex-1 min-w-0\">\r\n <span className=\"font-medium truncate\">{location.name}</span>\r\n <span className=\"text-muted-foreground text-sm truncate\">\r\n {location.region}\r\n </span>\r\n </div>\r\n </div>\r\n </DropdownMenuItem>\r\n )\r\n })}\r\n </>\r\n )}\r\n\r\n {/* No results at all */}\r\n {filteredAttachableBuildings.length === 0 && filteredGeocodedSuggestions.length === 0 && (\r\n <DropdownMenuItem disabled>\r\n No buildings or addresses found\r\n </DropdownMenuItem>\r\n )}\r\n </>\r\n )}\r\n </DropdownMenuContent>\r\n </DropdownMenu>\r\n </div>\r\n\r\n {previewBuildings.length > 0 && editing && (\r\n <div className=\"mb-4 p-3 bg-muted rounded-md border border-border\">\r\n <p className=\"text-sm text-foreground\">\r\n <strong>{previewBuildings.length}</strong>\r\n {' '}\r\n {t('msg1')}\r\n {previewBuildings.length > 1 ? 's' : ''}\r\n {' '}\r\n {t('msg2')}\r\n </p>\r\n </div>\r\n )}\r\n\r\n <div className=\"py-4\">\r\n <DataTable\r\n columns={useAssociatedBuildingsColumns()}\r\n data={filteredBuildings || []}\r\n onRowClick={onRowClick}\r\n className=\"\"\r\n showPagination={true}\r\n />\r\n </div>\r\n </div>\r\n )\r\n}\r\n\r\nexport default AssociatedBuildingsTable"],"mappings":"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","names":["_a"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Viewer.d.ts","sourceRoot":"","sources":["../../../../src/core/components/viewers/Viewer.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;
|
|
1
|
+
{"version":3,"file":"Viewer.d.ts","sourceRoot":"","sources":["../../../../src/core/components/viewers/Viewer.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAa9B,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAA;AAsClD,UAAU,WAAW;IACnB,YAAY,EAAE,YAAY,CAAA;CAC3B;AAED,wBAAgB,MAAM,CAAC,EAAE,YAAY,EAAE,EAAE,WAAW,qBAyHnD"}
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
3
3
|
import * as React from "react";
|
|
4
4
|
import dynamic from "next/dynamic";
|
|
5
|
+
import { Loader2 } from "lucide-react";
|
|
5
6
|
import { useSearchParams, useRouter, usePathname } from "next/navigation";
|
|
6
7
|
import { ViewerNames } from "../../types";
|
|
7
8
|
import { BuildingsContext, MenusContext } from "../../store";
|
|
@@ -23,7 +24,10 @@ const DataMenu = dynamic(
|
|
|
23
24
|
{ ssr: false, loading: () => /* @__PURE__ */ jsx(ViewerLoadingFallback, { label: "Loading data\u2026" }) }
|
|
24
25
|
);
|
|
25
26
|
function ViewerLoadingFallback({ label }) {
|
|
26
|
-
return /* @__PURE__ */ jsx("div", {
|
|
27
|
+
return /* @__PURE__ */ jsx("div", { className: "absolute inset-0 z-10 flex items-center justify-center bg-background/50 backdrop-blur-[1px]", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3 rounded-md border border-border bg-card px-5 py-4 shadow-sm", children: [
|
|
28
|
+
/* @__PURE__ */ jsx(Loader2, { className: "h-5 w-5 animate-spin text-muted-foreground" }),
|
|
29
|
+
/* @__PURE__ */ jsx("span", { className: "text-foreground text-base font-medium font-['Inter'] leading-7", children: label })
|
|
30
|
+
] }) });
|
|
27
31
|
}
|
|
28
32
|
function Viewer({ organization }) {
|
|
29
33
|
const searchParams = useSearchParams();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../src/core/components/viewers/Viewer.tsx"],"sourcesContent":["'use client'\r\n\r\n// Dependencies\r\nimport * as React from 'react'\r\nimport dynamic from 'next/dynamic'\r\nimport { useSearchParams, useRouter, usePathname } from 'next/navigation'\r\nimport { ViewerNames } from '../../types'\r\nimport { BuildingsContext, MenusContext } from '../../store'\r\nimport { MapViewer } from './map/MapViewer'\r\n// Audit Phase 1.F (F-2c): direct file import to bypass the ui barrel\r\n// (`src/core/components/ui/index.ts`), which re-exports `InfoSidebar` and\r\n// `useFileUploadHandler` and other heavy modules. Viewer.tsx is on the\r\n// eager map route, so any pull-through-the-barrel here lands in the\r\n// /[instance] First Load JS.\r\nimport { SidebarTrigger } from '../ui/Sidebar'\r\nimport { UserSettings } from '../settings'\r\nimport { Toolbar } from '../Toolbar'\r\nimport { switchLanguage } from '../../utils/utils'\r\nimport { Organization } from '../../types/dbTypes'\r\n\r\n// Audit Phase 1.A (F-1): code-split heavy viewers. BimViewer pulls in\r\n// ~456 KB gzipped of @thatopen/*; PointCloudViewer pulls in the Potree\r\n// loader stack. Both are loaded only when the user actually selects them,\r\n// so map-only sessions never pay their cost. MapViewer stays statically\r\n// imported 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// Audit Phase 1.B (F-13): DataMenu transitively imports FilePreview which\r\n// imports BimViewer directly (not via the dynamic wrapper above) — so the\r\n// previous static import of DataMenu re-introduced @thatopen + three/webgpu\r\n// into the eager bundle. Now lazy alongside BimViewer / PointCloudViewer.\r\n// DataMenu is only rendered when the user picks viewer=buildings|sites|\r\n// files|land|infrastructure|extensions|users, 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\nfunction ViewerLoadingFallback({ label }: { label: string }) {\r\n return (\r\n <div style={{ width: '100%', height: '100%', display: 'flex', alignItems: 'center', justifyContent: 'center', color: '#888' }}>\r\n {label}\r\n </div>\r\n )\r\n}\r\n\r\ninterface ViewerProps {\r\n organization: Organization\r\n}\r\n\r\nexport function Viewer({ organization }: 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} />\r\n </div>\r\n {validViewer === ViewerNames.bim && <BimViewer />}\r\n {validViewer === ViewerNames.pointcloud && <PointCloudViewer />}\r\n {[ViewerNames.buildings, ViewerNames.sites, ViewerNames.files, ViewerNames.land, ViewerNames.infrastructure, ViewerNames.extensions, ViewerNames.users].includes(validViewer) && (\r\n <DataMenu currentViewer={validViewer} />\r\n )}\r\n {[ViewerNames.settings].includes(validViewer) && (\r\n <UserSettings />\r\n )}\r\n </div>\r\n </>\r\n )\r\n\r\n return (\r\n <>\r\n {selectedViewer}\r\n <Toolbar viewer={validViewer} />\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// Dependencies\r\nimport * as React from 'react'\r\nimport dynamic from 'next/dynamic'\r\nimport { Loader2 } from 'lucide-react'\r\nimport { useSearchParams, useRouter, usePathname } from 'next/navigation'\r\nimport { ViewerNames } from '../../types'\r\nimport { BuildingsContext, MenusContext } from '../../store'\r\nimport { MapViewer } from './map/MapViewer'\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 { SidebarTrigger } from '../ui/Sidebar'\r\nimport { UserSettings } from '../settings'\r\nimport { Toolbar } from '../Toolbar'\r\nimport { switchLanguage } from '../../utils/utils'\r\nimport { 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}\r\n\r\nexport function Viewer({ organization }: 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} />\r\n </div>\r\n {validViewer === ViewerNames.bim && <BimViewer />}\r\n {validViewer === ViewerNames.pointcloud && <PointCloudViewer />}\r\n {[ViewerNames.buildings, ViewerNames.sites, ViewerNames.files, ViewerNames.land, ViewerNames.infrastructure, ViewerNames.extensions, ViewerNames.users].includes(validViewer) && (\r\n <DataMenu currentViewer={validViewer} />\r\n )}\r\n {[ViewerNames.settings].includes(validViewer) && (\r\n <UserSettings />\r\n )}\r\n </div>\r\n </>\r\n )\r\n\r\n return (\r\n <>\r\n {selectedViewer}\r\n <Toolbar viewer={validViewer} />\r\n </>\r\n )\r\n}"],"mappings":"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","names":[]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"BimToolbar.d.ts","sourceRoot":"","sources":["../../../../../src/core/components/viewers/bim/BimToolbar.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;
|
|
1
|
+
{"version":3,"file":"BimToolbar.d.ts","sourceRoot":"","sources":["../../../../../src/core/components/viewers/bim/BimToolbar.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAI9B,wBAAgB,UAAU,sBAEzB"}
|