@collabdt/core 0.0.55 → 0.0.57
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/AppSidebarContent.d.ts +2 -1
- package/dist/core/components/AppSidebarContent.d.ts.map +1 -1
- package/dist/core/components/AppSidebarContent.js +8 -5
- package/dist/core/components/AppSidebarContent.js.map +1 -1
- package/dist/core/components/Toolbar.d.ts +7 -1
- package/dist/core/components/Toolbar.d.ts.map +1 -1
- package/dist/core/components/Toolbar.js +3 -3
- package/dist/core/components/Toolbar.js.map +1 -1
- package/dist/core/components/TopNavigationBar.d.ts +6 -1
- package/dist/core/components/TopNavigationBar.d.ts.map +1 -1
- package/dist/core/components/TopNavigationBar.js +2 -2
- package/dist/core/components/TopNavigationBar.js.map +1 -1
- package/dist/core/components/authentication/AuthPage.d.ts +2 -1
- package/dist/core/components/authentication/AuthPage.d.ts.map +1 -1
- package/dist/core/components/authentication/AuthPage.js +2 -3
- package/dist/core/components/authentication/AuthPage.js.map +1 -1
- package/dist/core/components/authentication/Signin.d.ts +6 -1
- package/dist/core/components/authentication/Signin.d.ts.map +1 -1
- package/dist/core/components/authentication/Signin.js +17 -6
- package/dist/core/components/authentication/Signin.js.map +1 -1
- package/dist/core/components/organizationConfig/src/organizationConfigContent.d.ts.map +1 -1
- package/dist/core/components/organizationConfig/src/organizationConfigContent.js +109 -36
- package/dist/core/components/organizationConfig/src/organizationConfigContent.js.map +1 -1
- package/dist/core/components/settings/index.d.ts +3 -1
- package/dist/core/components/settings/index.d.ts.map +1 -1
- package/dist/core/components/settings/index.js +2 -2
- package/dist/core/components/settings/index.js.map +1 -1
- package/dist/core/components/settings/src/AccountSettingsPanel.d.ts.map +1 -1
- package/dist/core/components/settings/src/AccountSettingsPanel.js +9 -2
- package/dist/core/components/settings/src/AccountSettingsPanel.js.map +1 -1
- package/dist/core/components/settings/src/OrganizationSettingsPanel.d.ts +5 -1
- package/dist/core/components/settings/src/OrganizationSettingsPanel.d.ts.map +1 -1
- package/dist/core/components/settings/src/OrganizationSettingsPanel.js +7 -3
- package/dist/core/components/settings/src/OrganizationSettingsPanel.js.map +1 -1
- package/dist/core/components/settings/src/SettingsContent.d.ts +2 -1
- package/dist/core/components/settings/src/SettingsContent.d.ts.map +1 -1
- package/dist/core/components/settings/src/SettingsContent.js +2 -2
- package/dist/core/components/settings/src/SettingsContent.js.map +1 -1
- package/dist/core/components/support/BugReportDialog.d.ts +3 -2
- package/dist/core/components/support/BugReportDialog.d.ts.map +1 -1
- package/dist/core/components/support/BugReportDialog.js +71 -25
- package/dist/core/components/support/BugReportDialog.js.map +1 -1
- package/dist/core/components/support/FeatureRequestDialog.d.ts +3 -1
- package/dist/core/components/support/FeatureRequestDialog.d.ts.map +1 -1
- package/dist/core/components/support/FeatureRequestDialog.js +7 -4
- package/dist/core/components/support/FeatureRequestDialog.js.map +1 -1
- package/dist/core/components/support/SupportMenu.d.ts.map +1 -1
- package/dist/core/components/support/SupportMenu.js +1 -4
- package/dist/core/components/support/SupportMenu.js.map +1 -1
- package/dist/core/components/ui/AnimatedBackground.d.ts +2 -1
- package/dist/core/components/ui/AnimatedBackground.d.ts.map +1 -1
- package/dist/core/components/ui/AnimatedBackground.js +2 -3
- package/dist/core/components/ui/AnimatedBackground.js.map +1 -1
- package/dist/core/components/ui/AppSidebar.d.ts.map +1 -1
- package/dist/core/components/ui/AppSidebar.js +11 -11
- package/dist/core/components/ui/AppSidebar.js.map +1 -1
- package/dist/core/components/ui/InfoSidebar/Container.d.ts +3 -1
- package/dist/core/components/ui/InfoSidebar/Container.d.ts.map +1 -1
- package/dist/core/components/ui/InfoSidebar/Container.js +4 -2
- package/dist/core/components/ui/InfoSidebar/Container.js.map +1 -1
- package/dist/core/components/ui/InfoSidebar/Header.d.ts +3 -2
- package/dist/core/components/ui/InfoSidebar/Header.d.ts.map +1 -1
- package/dist/core/components/ui/InfoSidebar/Header.js +5 -6
- package/dist/core/components/ui/InfoSidebar/Header.js.map +1 -1
- package/dist/core/components/ui/InfoSidebar/index.d.ts +7 -1
- package/dist/core/components/ui/InfoSidebar/index.d.ts.map +1 -1
- package/dist/core/components/ui/InfoSidebar/index.js +4 -4
- package/dist/core/components/ui/InfoSidebar/index.js.map +1 -1
- package/dist/core/components/ui/Sensors/CollapsibleSensorItem.d.ts +2 -1
- package/dist/core/components/ui/Sensors/CollapsibleSensorItem.d.ts.map +1 -1
- package/dist/core/components/ui/Sensors/CollapsibleSensorItem.js +3 -2
- package/dist/core/components/ui/Sensors/CollapsibleSensorItem.js.map +1 -1
- package/dist/core/components/ui/Sensors/SensorsSection.d.ts +3 -1
- package/dist/core/components/ui/Sensors/SensorsSection.d.ts.map +1 -1
- package/dist/core/components/ui/Sensors/SensorsSection.js +3 -1
- package/dist/core/components/ui/Sensors/SensorsSection.js.map +1 -1
- package/dist/core/components/ui/Sidebar.d.ts +8 -0
- package/dist/core/components/ui/Sidebar.d.ts.map +1 -1
- package/dist/core/components/ui/Sidebar.js +21 -7
- package/dist/core/components/ui/Sidebar.js.map +1 -1
- package/dist/core/components/ui/ToolbarButton.js +18 -1
- package/dist/core/components/ui/ToolbarButton.js.map +1 -1
- package/dist/core/components/viewers/Data/DataMenu.d.ts +5 -1
- package/dist/core/components/viewers/Data/DataMenu.d.ts.map +1 -1
- package/dist/core/components/viewers/Data/DataMenu.js +6 -3
- package/dist/core/components/viewers/Data/DataMenu.js.map +1 -1
- package/dist/core/components/viewers/Data/HeaderButtons.d.ts +3 -1
- package/dist/core/components/viewers/Data/HeaderButtons.d.ts.map +1 -1
- package/dist/core/components/viewers/Data/HeaderButtons.js +6 -2
- package/dist/core/components/viewers/Data/HeaderButtons.js.map +1 -1
- package/dist/core/components/viewers/Data/buildingDetails/AddBuilding.d.ts +3 -1
- package/dist/core/components/viewers/Data/buildingDetails/AddBuilding.d.ts.map +1 -1
- package/dist/core/components/viewers/Data/buildingDetails/AddBuilding.js +4 -2
- package/dist/core/components/viewers/Data/buildingDetails/AddBuilding.js.map +1 -1
- package/dist/core/components/viewers/Data/buildingDetails/GeocoderInput.d.ts +3 -1
- package/dist/core/components/viewers/Data/buildingDetails/GeocoderInput.d.ts.map +1 -1
- package/dist/core/components/viewers/Data/buildingDetails/GeocoderInput.js +3 -1
- package/dist/core/components/viewers/Data/buildingDetails/GeocoderInput.js.map +1 -1
- package/dist/core/components/viewers/Data/details/DetailHeader.d.ts +2 -1
- package/dist/core/components/viewers/Data/details/DetailHeader.d.ts.map +1 -1
- package/dist/core/components/viewers/Data/details/DetailHeader.js +3 -5
- package/dist/core/components/viewers/Data/details/DetailHeader.js.map +1 -1
- package/dist/core/components/viewers/Viewer.d.ts +7 -1
- package/dist/core/components/viewers/Viewer.d.ts.map +1 -1
- package/dist/core/components/viewers/Viewer.js +6 -6
- package/dist/core/components/viewers/Viewer.js.map +1 -1
- package/dist/core/components/viewers/bim/BimToolbar.d.ts +3 -1
- package/dist/core/components/viewers/bim/BimToolbar.d.ts.map +1 -1
- package/dist/core/components/viewers/bim/BimToolbar.js +2 -2
- package/dist/core/components/viewers/bim/BimToolbar.js.map +1 -1
- package/dist/core/components/viewers/bim/src/BimSidebar/src/SensorsTab/index.d.ts +3 -1
- package/dist/core/components/viewers/bim/src/BimSidebar/src/SensorsTab/index.d.ts.map +1 -1
- package/dist/core/components/viewers/bim/src/BimSidebar/src/SensorsTab/index.js +2 -2
- package/dist/core/components/viewers/bim/src/BimSidebar/src/SensorsTab/index.js.map +1 -1
- package/dist/core/components/viewers/bim/src/BimSidebar/src/index.d.ts +5 -1
- package/dist/core/components/viewers/bim/src/BimSidebar/src/index.d.ts.map +1 -1
- package/dist/core/components/viewers/bim/src/BimSidebar/src/index.js +3 -2
- package/dist/core/components/viewers/bim/src/BimSidebar/src/index.js.map +1 -1
- package/dist/core/components/viewers/bim/src/tools/AddToBim/index.d.ts +3 -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 +2 -2
- package/dist/core/components/viewers/bim/src/tools/AddToBim/index.js.map +1 -1
- package/dist/core/components/viewers/bim/src/tools/AddToBim/src/useSensorMarkers.d.ts +1 -1
- package/dist/core/components/viewers/bim/src/tools/AddToBim/src/useSensorMarkers.d.ts.map +1 -1
- package/dist/core/components/viewers/bim/src/tools/AddToBim/src/useSensorMarkers.js +2 -2
- package/dist/core/components/viewers/bim/src/tools/AddToBim/src/useSensorMarkers.js.map +1 -1
- package/dist/core/components/viewers/bim/src/tools/bimToolbar.d.ts +3 -1
- package/dist/core/components/viewers/bim/src/tools/bimToolbar.d.ts.map +1 -1
- package/dist/core/components/viewers/bim/src/tools/bimToolbar.js +4 -2
- package/dist/core/components/viewers/bim/src/tools/bimToolbar.js.map +1 -1
- package/dist/core/components/viewers/map/MapViewer.d.ts +3 -1
- package/dist/core/components/viewers/map/MapViewer.d.ts.map +1 -1
- package/dist/core/components/viewers/map/MapViewer.js +2 -2
- package/dist/core/components/viewers/map/MapViewer.js.map +1 -1
- package/dist/core/components/viewers/map/datasets/RowActions.d.ts +2 -1
- package/dist/core/components/viewers/map/datasets/RowActions.d.ts.map +1 -1
- package/dist/core/components/viewers/map/datasets/RowActions.js +5 -4
- package/dist/core/components/viewers/map/datasets/RowActions.js.map +1 -1
- package/dist/core/components/viewers/map/datasets/index.d.ts +5 -1
- package/dist/core/components/viewers/map/datasets/index.d.ts.map +1 -1
- package/dist/core/components/viewers/map/datasets/index.js +15 -16
- package/dist/core/components/viewers/map/datasets/index.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 +3 -5
- package/dist/core/components/viewers/map/datasets/src/minioDatasets.js.map +1 -1
- package/dist/core/components/viewers/map/src/Geocoder.d.ts +3 -1
- package/dist/core/components/viewers/map/src/Geocoder.d.ts.map +1 -1
- package/dist/core/components/viewers/map/src/Geocoder.js +5 -1
- package/dist/core/components/viewers/map/src/Geocoder.js.map +1 -1
- package/dist/core/components/viewers/map/src/MapLayers/index.d.ts +6 -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 +3 -3
- package/dist/core/components/viewers/map/src/MapLayers/index.js.map +1 -1
- package/dist/core/components/viewers/map/src/MapLayers/src/CountryLayer/index.d.ts +5 -1
- 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 +6 -10
- package/dist/core/components/viewers/map/src/MapLayers/src/CountryLayer/index.js.map +1 -1
- package/dist/core/components/viewers/map/src/MapLayers/src/SensorsLayer/index.d.ts +3 -1
- package/dist/core/components/viewers/map/src/MapLayers/src/SensorsLayer/index.d.ts.map +1 -1
- package/dist/core/components/viewers/map/src/MapLayers/src/SensorsLayer/index.js +2 -2
- package/dist/core/components/viewers/map/src/MapLayers/src/SensorsLayer/index.js.map +1 -1
- package/dist/core/components/viewers/map/src/MapSidebar/index.d.ts +6 -1
- package/dist/core/components/viewers/map/src/MapSidebar/index.d.ts.map +1 -1
- package/dist/core/components/viewers/map/src/MapSidebar/index.js +5 -4
- package/dist/core/components/viewers/map/src/MapSidebar/index.js.map +1 -1
- package/dist/core/components/viewers/map/src/MapSidebar/src/LayersTab/index.d.ts +5 -1
- package/dist/core/components/viewers/map/src/MapSidebar/src/LayersTab/index.d.ts.map +1 -1
- package/dist/core/components/viewers/map/src/MapSidebar/src/LayersTab/index.js +5 -8
- package/dist/core/components/viewers/map/src/MapSidebar/src/LayersTab/index.js.map +1 -1
- package/dist/core/components/viewers/map/src/MapSidebar/src/SensorsTab/index.d.ts +3 -1
- package/dist/core/components/viewers/map/src/MapSidebar/src/SensorsTab/index.d.ts.map +1 -1
- package/dist/core/components/viewers/map/src/MapSidebar/src/SensorsTab/index.js +2 -2
- package/dist/core/components/viewers/map/src/MapSidebar/src/SensorsTab/index.js.map +1 -1
- package/dist/core/components/viewers/map/src/MapSidebar/src/SettingsTab/index.d.ts +3 -1
- package/dist/core/components/viewers/map/src/MapSidebar/src/SettingsTab/index.d.ts.map +1 -1
- package/dist/core/components/viewers/map/src/MapSidebar/src/SettingsTab/index.js +2 -2
- package/dist/core/components/viewers/map/src/MapSidebar/src/SettingsTab/index.js.map +1 -1
- package/dist/core/components/viewers/map/src/MapSidebar/src/SettingsTab/src/LocationSettings.d.ts +3 -1
- package/dist/core/components/viewers/map/src/MapSidebar/src/SettingsTab/src/LocationSettings.d.ts.map +1 -1
- package/dist/core/components/viewers/map/src/MapSidebar/src/SettingsTab/src/LocationSettings.js +4 -7
- package/dist/core/components/viewers/map/src/MapSidebar/src/SettingsTab/src/LocationSettings.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 +8 -8
- package/dist/core/components/viewers/map/src/MapSidebar/src/SettingsTab/src/TerrainLevel.js.map +1 -1
- package/dist/core/components/viewers/map/src/tools/AddTools/AddToMap.d.ts +8 -1
- package/dist/core/components/viewers/map/src/tools/AddTools/AddToMap.d.ts.map +1 -1
- package/dist/core/components/viewers/map/src/tools/AddTools/AddToMap.js +8 -3
- package/dist/core/components/viewers/map/src/tools/AddTools/AddToMap.js.map +1 -1
- package/dist/core/components/viewers/map/src/tools/DatasetMapTool.d.ts +11 -1
- package/dist/core/components/viewers/map/src/tools/DatasetMapTool.d.ts.map +1 -1
- package/dist/core/components/viewers/map/src/tools/DatasetMapTool.js +5 -3
- package/dist/core/components/viewers/map/src/tools/DatasetMapTool.js.map +1 -1
- package/dist/core/components/viewers/map/src/tools/mapTools.d.ts +10 -1
- package/dist/core/components/viewers/map/src/tools/mapTools.d.ts.map +1 -1
- package/dist/core/components/viewers/map/src/tools/mapTools.js +5 -3
- package/dist/core/components/viewers/map/src/tools/mapTools.js.map +1 -1
- package/dist/core/components/viewers/map/utils/geocoding/config.d.ts +14 -5
- package/dist/core/components/viewers/map/utils/geocoding/config.d.ts.map +1 -1
- package/dist/core/components/viewers/map/utils/geocoding/config.js +22 -11
- package/dist/core/components/viewers/map/utils/geocoding/config.js.map +1 -1
- package/dist/core/components/viewers/map/utils/geocoding/index.d.ts.map +1 -1
- package/dist/core/components/viewers/map/utils/geocoding/index.js +2 -2
- package/dist/core/components/viewers/map/utils/geocoding/index.js.map +1 -1
- package/dist/core/components/viewers/map/utils/geocoding/osm.d.ts.map +1 -1
- package/dist/core/components/viewers/map/utils/geocoding/osm.js +5 -4
- package/dist/core/components/viewers/map/utils/geocoding/osm.js.map +1 -1
- package/dist/core/components/viewers/map/utils/geocoding/pelias.d.ts.map +1 -1
- package/dist/core/components/viewers/map/utils/geocoding/pelias.js +7 -5
- package/dist/core/components/viewers/map/utils/geocoding/pelias.js.map +1 -1
- package/dist/core/components/viewers/pointcloud/PointCloudViewer.d.ts +3 -1
- package/dist/core/components/viewers/pointcloud/PointCloudViewer.d.ts.map +1 -1
- package/dist/core/components/viewers/pointcloud/PointCloudViewer.js +11 -12
- package/dist/core/components/viewers/pointcloud/PointCloudViewer.js.map +1 -1
- package/dist/core/components/viewers/pointcloud/src/PointCloudLoadingState/index.d.ts +3 -1
- package/dist/core/components/viewers/pointcloud/src/PointCloudLoadingState/index.d.ts.map +1 -1
- package/dist/core/components/viewers/pointcloud/src/PointCloudLoadingState/index.js +37 -50
- package/dist/core/components/viewers/pointcloud/src/PointCloudLoadingState/index.js.map +1 -1
- package/dist/core/components/viewers/pointcloud/src/PointCloudManagement/PointCloudManagementTable.d.ts +3 -1
- package/dist/core/components/viewers/pointcloud/src/PointCloudManagement/PointCloudManagementTable.d.ts.map +1 -1
- package/dist/core/components/viewers/pointcloud/src/PointCloudManagement/PointCloudManagementTable.js +5 -3
- package/dist/core/components/viewers/pointcloud/src/PointCloudManagement/PointCloudManagementTable.js.map +1 -1
- package/dist/core/components/viewers/pointcloud/src/PointCloudManagement/PointCloudTable.d.ts +2 -1
- package/dist/core/components/viewers/pointcloud/src/PointCloudManagement/PointCloudTable.d.ts.map +1 -1
- package/dist/core/components/viewers/pointcloud/src/PointCloudManagement/PointCloudTable.js +8 -7
- package/dist/core/components/viewers/pointcloud/src/PointCloudManagement/PointCloudTable.js.map +1 -1
- package/dist/core/components/viewers/pointcloud/src/PointCloudManagement/PointCloudUploadPanel.d.ts +2 -1
- package/dist/core/components/viewers/pointcloud/src/PointCloudManagement/PointCloudUploadPanel.d.ts.map +1 -1
- package/dist/core/components/viewers/pointcloud/src/PointCloudManagement/PointCloudUploadPanel.js +22 -9
- package/dist/core/components/viewers/pointcloud/src/PointCloudManagement/PointCloudUploadPanel.js.map +1 -1
- package/dist/core/components/viewers/pointcloud/src/PointCloudSidebar/index.d.ts +5 -1
- package/dist/core/components/viewers/pointcloud/src/PointCloudSidebar/index.d.ts.map +1 -1
- package/dist/core/components/viewers/pointcloud/src/PointCloudSidebar/index.js +3 -2
- package/dist/core/components/viewers/pointcloud/src/PointCloudSidebar/index.js.map +1 -1
- package/dist/core/components/viewers/pointcloud/src/PointCloudSidebar/src/FileTab/index.d.ts +3 -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 -2
- 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/PointCloudSection.d.ts +2 -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 +12 -22
- package/dist/core/components/viewers/pointcloud/src/PointCloudSidebar/src/FileTab/src/PointCloudSection.js.map +1 -1
- package/dist/core/hooks/useCaptureScreenshot.d.ts +2 -0
- package/dist/core/hooks/useCaptureScreenshot.d.ts.map +1 -0
- package/dist/core/hooks/useCaptureScreenshot.js +63 -0
- package/dist/core/hooks/useCaptureScreenshot.js.map +1 -0
- package/dist/core/hooks/useGeocodingRuntimeConfig.d.ts +3 -0
- package/dist/core/hooks/useGeocodingRuntimeConfig.d.ts.map +1 -0
- package/dist/core/hooks/useGeocodingRuntimeConfig.js +17 -0
- package/dist/core/hooks/useGeocodingRuntimeConfig.js.map +1 -0
- package/dist/core/store/AppConfig/context.d.ts +15 -1
- package/dist/core/store/AppConfig/context.d.ts.map +1 -1
- package/dist/core/store/AppConfig/context.js +26 -5
- package/dist/core/store/AppConfig/context.js.map +1 -1
- package/dist/core/store/CombineProviders.d.ts +6 -1
- package/dist/core/store/CombineProviders.d.ts.map +1 -1
- package/dist/core/store/CombineProviders.js +4 -3
- package/dist/core/store/CombineProviders.js.map +1 -1
- package/dist/core/types/tools.d.ts +2 -1
- package/dist/core/types/tools.d.ts.map +1 -1
- package/dist/core/utils/imageUtils.d.ts.map +1 -1
- package/dist/core/utils/imageUtils.js +2 -1
- package/dist/core/utils/imageUtils.js.map +1 -1
- package/package.json +1 -1
|
@@ -31,11 +31,15 @@ import { Search } from "lucide-react";
|
|
|
31
31
|
import { useBuildings } from "../../../../hooks/buildings/buildings";
|
|
32
32
|
import { usePathname } from "next/navigation";
|
|
33
33
|
import { LoadingSpinner } from "../../../ui/LoadingSpinner";
|
|
34
|
+
import { useGeocodingRuntimeConfig } from "../../../../hooks/useGeocodingRuntimeConfig";
|
|
34
35
|
function Geocoder({
|
|
35
36
|
onSelect,
|
|
36
37
|
showDatabaseBuildings = true,
|
|
37
|
-
alwaysExpanded = false
|
|
38
|
+
alwaysExpanded = false,
|
|
39
|
+
geocodeEarthApiKey,
|
|
40
|
+
geocoderUrl
|
|
38
41
|
}) {
|
|
42
|
+
useGeocodingRuntimeConfig({ geocodeEarthApiKey, geocoderUrl });
|
|
39
43
|
const t = useTranslations("Geocoder");
|
|
40
44
|
const [address, setAddress] = React.useState("");
|
|
41
45
|
const [query, setQuery] = React.useState("");
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../src/core/components/viewers/map/src/Geocoder.tsx"],"sourcesContent":["'use client'\r\n\r\n// SPDX-License-Identifier: AGPL-3.0-or-later\r\n// Copyright (C) 2025 Collab Digital Twins\r\n\r\n// Dependencies\r\nimport * as React from 'react'\r\nimport { useTranslations } from 'next-intl'\r\n\r\n// Context\r\nimport { MapContext } from '../../../../store'\r\n\r\n// Utility functions\r\nimport { fetchSuggestions, parseLocation, handleLocationSelect, buildingToFeature } from '../utils/geocoder'\r\n\r\n// Import the shadcn components\r\nimport { Command, CommandList, CommandEmpty, CommandGroup, CommandItem, CommandSeparator } from '../../../ui/Command'\r\nimport { Button } from '../../../ui/Button'\r\nimport { Input } from '../../../ui/Input'\r\nimport { Separator } from '../../../ui/Separator'\r\n\r\n// Icons\r\nimport { Search } from 'lucide-react'\r\n\r\n// Building data hook\r\nimport { useBuildings } from '../../../../hooks/buildings/buildings'\r\n\r\nimport { usePathname } from 'next/navigation'\r\nimport type { CurrentLocation } from '../../../../types/map'\r\nimport { LoadingSpinner } from '../../../ui/LoadingSpinner'\r\n\r\ninterface GeocoderProps {\r\n onSelect?: (addressData: any) => void\r\n showDatabaseBuildings?: boolean\r\n alwaysExpanded?: boolean\r\n}\r\n\r\nexport default function Geocoder({\r\n onSelect,\r\n showDatabaseBuildings = true,\r\n alwaysExpanded = false,\r\n}: GeocoderProps) {\r\n // Translation\r\n const t = useTranslations('Geocoder')\r\n\r\n const [address, setAddress] = React.useState<string>('')\r\n const [query, setQuery] = React.useState('')\r\n const [suggestions, setSuggestions] = React.useState([])\r\n const [buildingResults, setBuildingResults] = React.useState([])\r\n const [focused, setFocused] = React.useState(alwaysExpanded)\r\n const [selectedAddress, setSelectedAddress] = React.useState(null)\r\n const [isExpanded, setIsExpanded] = React.useState(alwaysExpanded)\r\n const [loading, setLoading] = React.useState(false)\r\n\r\n const pathname = usePathname()\r\n\r\n const suggestionsRef = React.useRef(null)\r\n const inputRef = React.useRef(null)\r\n const debounceRef = React.useRef(null)\r\n\r\n const { dispatch: mapDispatch, state: mapState } = React.useContext(MapContext)\r\n const { map, currentLocation, organizationCountry } = mapState.map\r\n\r\n // Fetch buildings data from API only if showDatabaseBuildings is true\r\n const { buildings, isLoading: isBuildingsLoading } = useBuildings()\r\n\r\n // Filter buildings based on search query\r\n const filterBuildings = React.useCallback((searchQuery) => {\r\n if (!showDatabaseBuildings || !buildings || !searchQuery || searchQuery.length < 3) return []\r\n\r\n const lowerQuery = searchQuery.toLowerCase()\r\n\r\n if (Array.isArray(buildings) && buildings.length > 0) {\r\n return buildings.filter((building) => {\r\n return (\r\n (building.buildingName && building.buildingName.toLowerCase().includes(lowerQuery))\r\n || (building.buildingAddress && building.buildingAddress.toLowerCase().includes(lowerQuery))\r\n || (building.buildingMunicipality && building.buildingMunicipality.toLowerCase().includes(lowerQuery))\r\n || (building.buildingCountrySubdivision && building.buildingCountrySubdivision.toLowerCase().includes(lowerQuery))\r\n )\r\n }).slice(0, 3) // Limit to 3 results\r\n }\r\n return []\r\n }, [buildings, showDatabaseBuildings])\r\n\r\n // Debounce the API request to avoid rate limiting and excessive requests\r\n React.useEffect(() => {\r\n if (debounceRef.current) {\r\n clearTimeout(debounceRef.current)\r\n }\r\n\r\n debounceRef.current = setTimeout(async () => {\r\n if (!query || query.length < 3) {\r\n setSuggestions([])\r\n setBuildingResults([])\r\n setLoading(false)\r\n return\r\n }\r\n\r\n setLoading(true)\r\n try {\r\n // Filter buildings from our database only if enabled\r\n if (showDatabaseBuildings) {\r\n const filteredBuildings = filterBuildings(query)\r\n setBuildingResults(filteredBuildings)\r\n }\r\n\r\n // Fetch geocoding suggestions\r\n const geocodeResults = await fetchSuggestions(query, organizationCountry)\r\n \r\n // Deduplicate geocode results based on name and region\r\n const deduplicatedResults = geocodeResults.reduce((acc, feature) => {\r\n const location = parseLocation(feature)\r\n // Create a unique key based on name and region (case-insensitive)\r\n const key = `${location.name?.toLowerCase() || ''}_${location.region?.toLowerCase() || ''}`\r\n \r\n // Only add if we haven't seen this combination before\r\n if (!acc.seen.has(key)) {\r\n acc.seen.add(key)\r\n acc.features.push(feature)\r\n }\r\n \r\n return acc\r\n }, { seen: new Set(), features: [] })\r\n \r\n setSuggestions(deduplicatedResults.features)\r\n }\r\n catch (error) {\r\n console.error('Error fetching suggestions:', error)\r\n setSuggestions([])\r\n }\r\n finally {\r\n setLoading(false)\r\n }\r\n }, 300) // Increased debounce time for better API rate limiting\r\n\r\n return () => {\r\n if (debounceRef.current) clearTimeout(debounceRef.current)\r\n }\r\n }, [query, filterBuildings, showDatabaseBuildings])\r\n\r\n // Handle click outside to close suggestions\r\n React.useEffect(() => {\r\n const handleClickOutside = (event) => {\r\n if (suggestionsRef.current && !suggestionsRef.current.contains(event.target)\r\n && inputRef.current && !inputRef.current.contains(event.target) // Don't close if alwaysExpanded is true\r\n && !alwaysExpanded) {\r\n setFocused(false)\r\n }\r\n }\r\n\r\n document.addEventListener('mousedown', handleClickOutside)\r\n return () => {\r\n document.removeEventListener('mousedown', handleClickOutside)\r\n }\r\n }, [alwaysExpanded])\r\n\r\n // Handle suggestion selection\r\n const handleSelectSuggestion = (feature, type = '') => {\r\n setQuery('') // Clear the input field\r\n setSuggestions([])\r\n setBuildingResults([])\r\n if (!alwaysExpanded) {\r\n setFocused(false)\r\n }\r\n\r\n // handleLocationSelect will automatically remove any existing marker and add a new one\r\n const addressData = handleLocationSelect(feature, map, type)\r\n\r\n const address = addressData.formatted_address || feature.properties?.label || ''\r\n setAddress(address)\r\n setSelectedAddress(addressData)\r\n\r\n if (onSelect) onSelect(addressData)\r\n\r\n if (mapDispatch && currentLocation) {\r\n const [lng, lat] = addressData.coordinates\r\n const props = addressData.properties || {}\r\n const parts = address.split(',').map(p => p.trim())\r\n // Read structured properties (provider-agnostic) instead of parsing label tokens,\r\n // which only matched Geocode Earth's \"…, ON, Canada\" abbreviation format and broke\r\n // on the Photon/Nominatim fallback.\r\n const countrySubdivision: string = props.region_a || props.region || ''\r\n const municipality: string = props.locality || props.neighbourhood || props.county || ''\r\n\r\n const newCurrentLocation: CurrentLocation = { ...currentLocation, municipality, countrySubdivision, longitude: lng, latitude: lat, address: parts[0] }\r\n mapDispatch({ type: 'UPDATE_LOCATION', payload: { currentLocation: newCurrentLocation } })\r\n if (municipality || countrySubdivision) {\r\n const url = new URL(window.location.href)\r\n // Write each field only when present, so an empty param doesn't shadow the\r\n // organization fallback MapViewer applies on reload.\r\n if (municipality) url.searchParams.set('municipality', municipality)\r\n else url.searchParams.delete('municipality')\r\n if (countrySubdivision) url.searchParams.set('countrySubdivision', countrySubdivision)\r\n else url.searchParams.delete('countrySubdivision')\r\n url.searchParams.set('address', parts[0])\r\n url.searchParams.set('lng', lng.toString())\r\n url.searchParams.set('lat', lat.toString())\r\n url.searchParams.set('zoom', '18')\r\n window.history.replaceState({}, '', url.toString())\r\n }\r\n }\r\n }\r\n\r\n // Handle expand of menubar to display search input\r\n const handleExpand = () => {\r\n if (alwaysExpanded) return // Don't allow toggling if always expanded\r\n\r\n setIsExpanded(!isExpanded)\r\n if (isExpanded) {\r\n setFocused(false)\r\n setQuery('') // clear input and suggestions when collapsed\r\n setSuggestions([])\r\n setBuildingResults([])\r\n }\r\n else {\r\n setFocused(true)\r\n setTimeout(() => {\r\n inputRef.current?.focus()\r\n }, 100) // Small delay to ensure the input is rendered\r\n }\r\n }\r\n\r\n return (\r\n <Command>\r\n {/* Search input + button */}\r\n <div className={`flex flex-row justify-start items-center w-full h-auto `}>\r\n {!alwaysExpanded && (\r\n <Button\r\n variant=\"ghost\"\r\n size=\"icon\"\r\n onClick={handleExpand}\r\n className=\"opacity-70 w-6 justify-end hover:opacity-100 transition-opacity duration-200 hover:bg-transparent\"\r\n >\r\n <Search />\r\n </Button>\r\n )}\r\n {alwaysExpanded && (\r\n <Search className=\"h-4 w-4 opacity-50\" />\r\n )}\r\n <Input\r\n ref={inputRef}\r\n type=\"text\"\r\n value={query}\r\n onChange={e => setQuery(e.target.value)}\r\n onFocus={() => setFocused(true)}\r\n onKeyDown={(e) => {\r\n if (e.key === 'Enter') {\r\n inputRef.current?.blur()\r\n }\r\n }}\r\n placeholder={alwaysExpanded ? t('placeholder1') : t('placeholder2')}\r\n className={`border-none focus-visible:ring-transparent text-sm transition-all ease-in-out duration-200 ${\r\n alwaysExpanded || isExpanded ? 'w-full' : 'w-0 p-0'\r\n }`}\r\n />\r\n </div>\r\n\r\n {focused && (loading || buildingResults.length > 0 || suggestions.length > 0) && (\r\n <div ref={suggestionsRef}>\r\n <Separator />\r\n <CommandList>\r\n {!loading && buildingResults.length === 0 && suggestions.length === 0 && query.length >= 3 && (\r\n <CommandEmpty>{showDatabaseBuildings ? t('noResults') : t('noAddresses')}</CommandEmpty>\r\n )}\r\n\r\n {loading && (\r\n <CommandEmpty><LoadingSpinner /></CommandEmpty>\r\n )}\r\n\r\n {/* Database Buildings section - only show if enabled */}\r\n {showDatabaseBuildings && !loading && buildingResults.length > 0 && (\r\n <CommandGroup heading={t('buildingHeader')}>\r\n {buildingResults.map((building) => {\r\n const buildingFeature = buildingToFeature(building)\r\n\r\n return (\r\n <CommandItem\r\n key={`building-${building.id}`}\r\n onSelect={() => handleSelectSuggestion(buildingFeature, 'dbBuilding')}\r\n className=\"cursor-pointer\"\r\n >\r\n <div className=\"font-medium\">{building.buildingName}</div>\r\n <div className=\"text-sm text-muted-foreground\">\r\n {building.buildingAddress}\r\n </div>\r\n </CommandItem>\r\n )\r\n })}\r\n </CommandGroup>\r\n )}\r\n\r\n {/* Separator between sections if both have results */}\r\n {showDatabaseBuildings && buildingResults.length > 0 && suggestions.length > 0 && (\r\n <CommandSeparator />\r\n )}\r\n\r\n {/* Geocoder suggestions section */}\r\n {!loading && suggestions.length > 0 && (\r\n <CommandGroup heading={showDatabaseBuildings ? t('suggestionsHeader') : undefined}>\r\n {suggestions.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 <CommandItem\r\n key={uniqueKey}\r\n onSelect={() => handleSelectSuggestion(feature)}\r\n className=\"cursor-pointer\"\r\n >\r\n <div className=\"font-medium\">{location.name}</div>\r\n <div className=\"text-sm text-muted-foreground\">{location.region}</div>\r\n </CommandItem>\r\n )\r\n })}\r\n </CommandGroup>\r\n )}\r\n </CommandList>\r\n </div>\r\n )}\r\n </Command>\r\n )\r\n}"],"mappings":"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","names":["address"]}
|
|
1
|
+
{"version":3,"sources":["../../../../../../src/core/components/viewers/map/src/Geocoder.tsx"],"sourcesContent":["'use client'\r\n\r\n// SPDX-License-Identifier: AGPL-3.0-or-later\r\n// Copyright (C) 2025 Collab Digital Twins\r\n\r\n// Dependencies\r\nimport * as React from 'react'\r\nimport { useTranslations } from 'next-intl'\r\n\r\n// Context\r\nimport { MapContext } from '../../../../store'\r\n\r\n// Utility functions\r\nimport { fetchSuggestions, parseLocation, handleLocationSelect, buildingToFeature } from '../utils/geocoder'\r\n\r\n// Import the shadcn components\r\nimport { Command, CommandList, CommandEmpty, CommandGroup, CommandItem, CommandSeparator } from '../../../ui/Command'\r\nimport { Button } from '../../../ui/Button'\r\nimport { Input } from '../../../ui/Input'\r\nimport { Separator } from '../../../ui/Separator'\r\n\r\n// Icons\r\nimport { Search } from 'lucide-react'\r\n\r\n// Building data hook\r\nimport { useBuildings } from '../../../../hooks/buildings/buildings'\r\n\r\nimport { usePathname } from 'next/navigation'\r\nimport type { CurrentLocation } from '../../../../types/map'\r\nimport { LoadingSpinner } from '../../../ui/LoadingSpinner'\r\nimport { useGeocodingRuntimeConfig } from '../../../../hooks/useGeocodingRuntimeConfig'\r\n\r\ninterface GeocoderProps {\r\n onSelect?: (addressData: any) => void\r\n showDatabaseBuildings?: boolean\r\n alwaysExpanded?: boolean\r\n geocodeEarthApiKey?: string\r\n geocoderUrl?: string\r\n}\r\n\r\nexport default function Geocoder({\r\n onSelect,\r\n showDatabaseBuildings = true,\r\n alwaysExpanded = false,\r\n geocodeEarthApiKey,\r\n geocoderUrl,\r\n}: GeocoderProps) {\r\n useGeocodingRuntimeConfig({ geocodeEarthApiKey, geocoderUrl })\r\n\r\n // Translation\r\n const t = useTranslations('Geocoder')\r\n\r\n const [address, setAddress] = React.useState<string>('')\r\n const [query, setQuery] = React.useState('')\r\n const [suggestions, setSuggestions] = React.useState([])\r\n const [buildingResults, setBuildingResults] = React.useState([])\r\n const [focused, setFocused] = React.useState(alwaysExpanded)\r\n const [selectedAddress, setSelectedAddress] = React.useState(null)\r\n const [isExpanded, setIsExpanded] = React.useState(alwaysExpanded)\r\n const [loading, setLoading] = React.useState(false)\r\n\r\n const pathname = usePathname()\r\n\r\n const suggestionsRef = React.useRef(null)\r\n const inputRef = React.useRef(null)\r\n const debounceRef = React.useRef(null)\r\n\r\n const { dispatch: mapDispatch, state: mapState } = React.useContext(MapContext)\r\n const { map, currentLocation, organizationCountry } = mapState.map\r\n\r\n // Fetch buildings data from API only if showDatabaseBuildings is true\r\n const { buildings, isLoading: isBuildingsLoading } = useBuildings()\r\n\r\n // Filter buildings based on search query\r\n const filterBuildings = React.useCallback((searchQuery) => {\r\n if (!showDatabaseBuildings || !buildings || !searchQuery || searchQuery.length < 3) return []\r\n\r\n const lowerQuery = searchQuery.toLowerCase()\r\n\r\n if (Array.isArray(buildings) && buildings.length > 0) {\r\n return buildings.filter((building) => {\r\n return (\r\n (building.buildingName && building.buildingName.toLowerCase().includes(lowerQuery))\r\n || (building.buildingAddress && building.buildingAddress.toLowerCase().includes(lowerQuery))\r\n || (building.buildingMunicipality && building.buildingMunicipality.toLowerCase().includes(lowerQuery))\r\n || (building.buildingCountrySubdivision && building.buildingCountrySubdivision.toLowerCase().includes(lowerQuery))\r\n )\r\n }).slice(0, 3) // Limit to 3 results\r\n }\r\n return []\r\n }, [buildings, showDatabaseBuildings])\r\n\r\n // Debounce the API request to avoid rate limiting and excessive requests\r\n React.useEffect(() => {\r\n if (debounceRef.current) {\r\n clearTimeout(debounceRef.current)\r\n }\r\n\r\n debounceRef.current = setTimeout(async () => {\r\n if (!query || query.length < 3) {\r\n setSuggestions([])\r\n setBuildingResults([])\r\n setLoading(false)\r\n return\r\n }\r\n\r\n setLoading(true)\r\n try {\r\n // Filter buildings from our database only if enabled\r\n if (showDatabaseBuildings) {\r\n const filteredBuildings = filterBuildings(query)\r\n setBuildingResults(filteredBuildings)\r\n }\r\n\r\n // Fetch geocoding suggestions\r\n const geocodeResults = await fetchSuggestions(query, organizationCountry)\r\n \r\n // Deduplicate geocode results based on name and region\r\n const deduplicatedResults = geocodeResults.reduce((acc, feature) => {\r\n const location = parseLocation(feature)\r\n // Create a unique key based on name and region (case-insensitive)\r\n const key = `${location.name?.toLowerCase() || ''}_${location.region?.toLowerCase() || ''}`\r\n \r\n // Only add if we haven't seen this combination before\r\n if (!acc.seen.has(key)) {\r\n acc.seen.add(key)\r\n acc.features.push(feature)\r\n }\r\n \r\n return acc\r\n }, { seen: new Set(), features: [] })\r\n \r\n setSuggestions(deduplicatedResults.features)\r\n }\r\n catch (error) {\r\n console.error('Error fetching suggestions:', error)\r\n setSuggestions([])\r\n }\r\n finally {\r\n setLoading(false)\r\n }\r\n }, 300) // Increased debounce time for better API rate limiting\r\n\r\n return () => {\r\n if (debounceRef.current) clearTimeout(debounceRef.current)\r\n }\r\n }, [query, filterBuildings, showDatabaseBuildings])\r\n\r\n // Handle click outside to close suggestions\r\n React.useEffect(() => {\r\n const handleClickOutside = (event) => {\r\n if (suggestionsRef.current && !suggestionsRef.current.contains(event.target)\r\n && inputRef.current && !inputRef.current.contains(event.target) // Don't close if alwaysExpanded is true\r\n && !alwaysExpanded) {\r\n setFocused(false)\r\n }\r\n }\r\n\r\n document.addEventListener('mousedown', handleClickOutside)\r\n return () => {\r\n document.removeEventListener('mousedown', handleClickOutside)\r\n }\r\n }, [alwaysExpanded])\r\n\r\n // Handle suggestion selection\r\n const handleSelectSuggestion = (feature, type = '') => {\r\n setQuery('') // Clear the input field\r\n setSuggestions([])\r\n setBuildingResults([])\r\n if (!alwaysExpanded) {\r\n setFocused(false)\r\n }\r\n\r\n // handleLocationSelect will automatically remove any existing marker and add a new one\r\n const addressData = handleLocationSelect(feature, map, type)\r\n\r\n const address = addressData.formatted_address || feature.properties?.label || ''\r\n setAddress(address)\r\n setSelectedAddress(addressData)\r\n\r\n if (onSelect) onSelect(addressData)\r\n\r\n if (mapDispatch && currentLocation) {\r\n const [lng, lat] = addressData.coordinates\r\n const props = addressData.properties || {}\r\n const parts = address.split(',').map(p => p.trim())\r\n // Read structured properties (provider-agnostic) instead of parsing label tokens,\r\n // which only matched Geocode Earth's \"…, ON, Canada\" abbreviation format and broke\r\n // on the Photon/Nominatim fallback.\r\n const countrySubdivision: string = props.region_a || props.region || ''\r\n const municipality: string = props.locality || props.neighbourhood || props.county || ''\r\n\r\n const newCurrentLocation: CurrentLocation = { ...currentLocation, municipality, countrySubdivision, longitude: lng, latitude: lat, address: parts[0] }\r\n mapDispatch({ type: 'UPDATE_LOCATION', payload: { currentLocation: newCurrentLocation } })\r\n if (municipality || countrySubdivision) {\r\n const url = new URL(window.location.href)\r\n // Write each field only when present, so an empty param doesn't shadow the\r\n // organization fallback MapViewer applies on reload.\r\n if (municipality) url.searchParams.set('municipality', municipality)\r\n else url.searchParams.delete('municipality')\r\n if (countrySubdivision) url.searchParams.set('countrySubdivision', countrySubdivision)\r\n else url.searchParams.delete('countrySubdivision')\r\n url.searchParams.set('address', parts[0])\r\n url.searchParams.set('lng', lng.toString())\r\n url.searchParams.set('lat', lat.toString())\r\n url.searchParams.set('zoom', '18')\r\n window.history.replaceState({}, '', url.toString())\r\n }\r\n }\r\n }\r\n\r\n // Handle expand of menubar to display search input\r\n const handleExpand = () => {\r\n if (alwaysExpanded) return // Don't allow toggling if always expanded\r\n\r\n setIsExpanded(!isExpanded)\r\n if (isExpanded) {\r\n setFocused(false)\r\n setQuery('') // clear input and suggestions when collapsed\r\n setSuggestions([])\r\n setBuildingResults([])\r\n }\r\n else {\r\n setFocused(true)\r\n setTimeout(() => {\r\n inputRef.current?.focus()\r\n }, 100) // Small delay to ensure the input is rendered\r\n }\r\n }\r\n\r\n return (\r\n <Command>\r\n {/* Search input + button */}\r\n <div className={`flex flex-row justify-start items-center w-full h-auto `}>\r\n {!alwaysExpanded && (\r\n <Button\r\n variant=\"ghost\"\r\n size=\"icon\"\r\n onClick={handleExpand}\r\n className=\"opacity-70 w-6 justify-end hover:opacity-100 transition-opacity duration-200 hover:bg-transparent\"\r\n >\r\n <Search />\r\n </Button>\r\n )}\r\n {alwaysExpanded && (\r\n <Search className=\"h-4 w-4 opacity-50\" />\r\n )}\r\n <Input\r\n ref={inputRef}\r\n type=\"text\"\r\n value={query}\r\n onChange={e => setQuery(e.target.value)}\r\n onFocus={() => setFocused(true)}\r\n onKeyDown={(e) => {\r\n if (e.key === 'Enter') {\r\n inputRef.current?.blur()\r\n }\r\n }}\r\n placeholder={alwaysExpanded ? t('placeholder1') : t('placeholder2')}\r\n className={`border-none focus-visible:ring-transparent text-sm transition-all ease-in-out duration-200 ${\r\n alwaysExpanded || isExpanded ? 'w-full' : 'w-0 p-0'\r\n }`}\r\n />\r\n </div>\r\n\r\n {focused && (loading || buildingResults.length > 0 || suggestions.length > 0) && (\r\n <div ref={suggestionsRef}>\r\n <Separator />\r\n <CommandList>\r\n {!loading && buildingResults.length === 0 && suggestions.length === 0 && query.length >= 3 && (\r\n <CommandEmpty>{showDatabaseBuildings ? t('noResults') : t('noAddresses')}</CommandEmpty>\r\n )}\r\n\r\n {loading && (\r\n <CommandEmpty><LoadingSpinner /></CommandEmpty>\r\n )}\r\n\r\n {/* Database Buildings section - only show if enabled */}\r\n {showDatabaseBuildings && !loading && buildingResults.length > 0 && (\r\n <CommandGroup heading={t('buildingHeader')}>\r\n {buildingResults.map((building) => {\r\n const buildingFeature = buildingToFeature(building)\r\n\r\n return (\r\n <CommandItem\r\n key={`building-${building.id}`}\r\n onSelect={() => handleSelectSuggestion(buildingFeature, 'dbBuilding')}\r\n className=\"cursor-pointer\"\r\n >\r\n <div className=\"font-medium\">{building.buildingName}</div>\r\n <div className=\"text-sm text-muted-foreground\">\r\n {building.buildingAddress}\r\n </div>\r\n </CommandItem>\r\n )\r\n })}\r\n </CommandGroup>\r\n )}\r\n\r\n {/* Separator between sections if both have results */}\r\n {showDatabaseBuildings && buildingResults.length > 0 && suggestions.length > 0 && (\r\n <CommandSeparator />\r\n )}\r\n\r\n {/* Geocoder suggestions section */}\r\n {!loading && suggestions.length > 0 && (\r\n <CommandGroup heading={showDatabaseBuildings ? t('suggestionsHeader') : undefined}>\r\n {suggestions.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 <CommandItem\r\n key={uniqueKey}\r\n onSelect={() => handleSelectSuggestion(feature)}\r\n className=\"cursor-pointer\"\r\n >\r\n <div className=\"font-medium\">{location.name}</div>\r\n <div className=\"text-sm text-muted-foreground\">{location.region}</div>\r\n </CommandItem>\r\n )\r\n })}\r\n </CommandGroup>\r\n )}\r\n </CommandList>\r\n </div>\r\n )}\r\n </Command>\r\n )\r\n}"],"mappings":";;;;;;;;;;;;;;;;;;;;AAyOU,SAQM,KARN;AAnOV,YAAY,WAAW;AACvB,SAAS,uBAAuB;AAGhC,SAAS,kBAAkB;AAG3B,SAAS,kBAAkB,eAAe,sBAAsB,yBAAyB;AAGzF,SAAS,SAAS,aAAa,cAAc,cAAc,aAAa,wBAAwB;AAChG,SAAS,cAAc;AACvB,SAAS,aAAa;AACtB,SAAS,iBAAiB;AAG1B,SAAS,cAAc;AAGvB,SAAS,oBAAoB;AAE7B,SAAS,mBAAmB;AAE5B,SAAS,sBAAsB;AAC/B,SAAS,iCAAiC;AAU3B,SAAR,SAA0B;AAAA,EAC/B;AAAA,EACA,wBAAwB;AAAA,EACxB,iBAAiB;AAAA,EACjB;AAAA,EACA;AACF,GAAkB;AAChB,4BAA0B,EAAE,oBAAoB,YAAY,CAAC;AAG7D,QAAM,IAAI,gBAAgB,UAAU;AAEpC,QAAM,CAAC,SAAS,UAAU,IAAI,MAAM,SAAiB,EAAE;AACvD,QAAM,CAAC,OAAO,QAAQ,IAAI,MAAM,SAAS,EAAE;AAC3C,QAAM,CAAC,aAAa,cAAc,IAAI,MAAM,SAAS,CAAC,CAAC;AACvD,QAAM,CAAC,iBAAiB,kBAAkB,IAAI,MAAM,SAAS,CAAC,CAAC;AAC/D,QAAM,CAAC,SAAS,UAAU,IAAI,MAAM,SAAS,cAAc;AAC3D,QAAM,CAAC,iBAAiB,kBAAkB,IAAI,MAAM,SAAS,IAAI;AACjE,QAAM,CAAC,YAAY,aAAa,IAAI,MAAM,SAAS,cAAc;AACjE,QAAM,CAAC,SAAS,UAAU,IAAI,MAAM,SAAS,KAAK;AAElD,QAAM,WAAW,YAAY;AAE7B,QAAM,iBAAiB,MAAM,OAAO,IAAI;AACxC,QAAM,WAAW,MAAM,OAAO,IAAI;AAClC,QAAM,cAAc,MAAM,OAAO,IAAI;AAErC,QAAM,EAAE,UAAU,aAAa,OAAO,SAAS,IAAI,MAAM,WAAW,UAAU;AAC9E,QAAM,EAAE,KAAK,iBAAiB,oBAAoB,IAAI,SAAS;AAG/D,QAAM,EAAE,WAAW,WAAW,mBAAmB,IAAI,aAAa;AAGlE,QAAM,kBAAkB,MAAM,YAAY,CAAC,gBAAgB;AACzD,QAAI,CAAC,yBAAyB,CAAC,aAAa,CAAC,eAAe,YAAY,SAAS,EAAG,QAAO,CAAC;AAE5F,UAAM,aAAa,YAAY,YAAY;AAE3C,QAAI,MAAM,QAAQ,SAAS,KAAK,UAAU,SAAS,GAAG;AACpD,aAAO,UAAU,OAAO,CAAC,aAAa;AACpC,eACG,SAAS,gBAAgB,SAAS,aAAa,YAAY,EAAE,SAAS,UAAU,KAC7E,SAAS,mBAAmB,SAAS,gBAAgB,YAAY,EAAE,SAAS,UAAU,KACtF,SAAS,wBAAwB,SAAS,qBAAqB,YAAY,EAAE,SAAS,UAAU,KAChG,SAAS,8BAA8B,SAAS,2BAA2B,YAAY,EAAE,SAAS,UAAU;AAAA,MAEpH,CAAC,EAAE,MAAM,GAAG,CAAC;AAAA,IACf;AACA,WAAO,CAAC;AAAA,EACV,GAAG,CAAC,WAAW,qBAAqB,CAAC;AAGrC,QAAM,UAAU,MAAM;AACpB,QAAI,YAAY,SAAS;AACvB,mBAAa,YAAY,OAAO;AAAA,IAClC;AAEA,gBAAY,UAAU,WAAW,YAAY;AAC3C,UAAI,CAAC,SAAS,MAAM,SAAS,GAAG;AAC9B,uBAAe,CAAC,CAAC;AACjB,2BAAmB,CAAC,CAAC;AACrB,mBAAW,KAAK;AAChB;AAAA,MACF;AAEA,iBAAW,IAAI;AACf,UAAI;AAEF,YAAI,uBAAuB;AACzB,gBAAM,oBAAoB,gBAAgB,KAAK;AAC/C,6BAAmB,iBAAiB;AAAA,QACtC;AAGA,cAAM,iBAAiB,MAAM,iBAAiB,OAAO,mBAAmB;AAGxE,cAAM,sBAAsB,eAAe,OAAO,CAAC,KAAK,YAAY;AAtH5E;AAuHU,gBAAM,WAAW,cAAc,OAAO;AAEtC,gBAAM,MAAM,KAAG,cAAS,SAAT,mBAAe,kBAAiB,EAAE,MAAI,cAAS,WAAT,mBAAiB,kBAAiB,EAAE;AAGzF,cAAI,CAAC,IAAI,KAAK,IAAI,GAAG,GAAG;AACtB,gBAAI,KAAK,IAAI,GAAG;AAChB,gBAAI,SAAS,KAAK,OAAO;AAAA,UAC3B;AAEA,iBAAO;AAAA,QACT,GAAG,EAAE,MAAM,oBAAI,IAAI,GAAG,UAAU,CAAC,EAAE,CAAC;AAEpC,uBAAe,oBAAoB,QAAQ;AAAA,MAC7C,SACO,OAAO;AACZ,gBAAQ,MAAM,+BAA+B,KAAK;AAClD,uBAAe,CAAC,CAAC;AAAA,MACnB,UACA;AACE,mBAAW,KAAK;AAAA,MAClB;AAAA,IACF,GAAG,GAAG;AAEN,WAAO,MAAM;AACX,UAAI,YAAY,QAAS,cAAa,YAAY,OAAO;AAAA,IAC3D;AAAA,EACF,GAAG,CAAC,OAAO,iBAAiB,qBAAqB,CAAC;AAGlD,QAAM,UAAU,MAAM;AACpB,UAAM,qBAAqB,CAAC,UAAU;AACpC,UAAI,eAAe,WAAW,CAAC,eAAe,QAAQ,SAAS,MAAM,MAAM,KACtE,SAAS,WAAW,CAAC,SAAS,QAAQ,SAAS,MAAM,MAAM,KAC3D,CAAC,gBAAgB;AACpB,mBAAW,KAAK;AAAA,MAClB;AAAA,IACF;AAEA,aAAS,iBAAiB,aAAa,kBAAkB;AACzD,WAAO,MAAM;AACX,eAAS,oBAAoB,aAAa,kBAAkB;AAAA,IAC9D;AAAA,EACF,GAAG,CAAC,cAAc,CAAC;AAGnB,QAAM,yBAAyB,CAAC,SAAS,OAAO,OAAO;AArKzD;AAsKI,aAAS,EAAE;AACX,mBAAe,CAAC,CAAC;AACjB,uBAAmB,CAAC,CAAC;AACrB,QAAI,CAAC,gBAAgB;AACnB,iBAAW,KAAK;AAAA,IAClB;AAGA,UAAM,cAAc,qBAAqB,SAAS,KAAK,IAAI;AAE3D,UAAMA,WAAU,YAAY,uBAAqB,aAAQ,eAAR,mBAAoB,UAAS;AAC9E,eAAWA,QAAO;AAClB,uBAAmB,WAAW;AAE9B,QAAI,SAAU,UAAS,WAAW;AAElC,QAAI,eAAe,iBAAiB;AAClC,YAAM,CAAC,KAAK,GAAG,IAAI,YAAY;AAC/B,YAAM,QAAQ,YAAY,cAAc,CAAC;AACzC,YAAM,QAAQA,SAAQ,MAAM,GAAG,EAAE,IAAI,OAAK,EAAE,KAAK,CAAC;AAIlD,YAAM,qBAA6B,MAAM,YAAY,MAAM,UAAU;AACrE,YAAM,eAAuB,MAAM,YAAY,MAAM,iBAAiB,MAAM,UAAU;AAEtF,YAAM,qBAAsC,iCAAK,kBAAL,EAAsB,cAAc,oBAAoB,WAAW,KAAK,UAAU,KAAK,SAAS,MAAM,CAAC,EAAE;AACrJ,kBAAY,EAAE,MAAM,mBAAmB,SAAS,EAAE,iBAAiB,mBAAmB,EAAE,CAAC;AACzF,UAAI,gBAAgB,oBAAoB;AACtC,cAAM,MAAM,IAAI,IAAI,OAAO,SAAS,IAAI;AAGxC,YAAI,aAAc,KAAI,aAAa,IAAI,gBAAgB,YAAY;AAAA,YAC9D,KAAI,aAAa,OAAO,cAAc;AAC3C,YAAI,mBAAoB,KAAI,aAAa,IAAI,sBAAsB,kBAAkB;AAAA,YAChF,KAAI,aAAa,OAAO,oBAAoB;AACjD,YAAI,aAAa,IAAI,WAAW,MAAM,CAAC,CAAC;AACxC,YAAI,aAAa,IAAI,OAAO,IAAI,SAAS,CAAC;AAC1C,YAAI,aAAa,IAAI,OAAO,IAAI,SAAS,CAAC;AAC1C,YAAI,aAAa,IAAI,QAAQ,IAAI;AACjC,eAAO,QAAQ,aAAa,CAAC,GAAG,IAAI,IAAI,SAAS,CAAC;AAAA,MACpD;AAAA,IACF;AAAA,EACF;AAGA,QAAM,eAAe,MAAM;AACzB,QAAI,eAAgB;AAEpB,kBAAc,CAAC,UAAU;AACzB,QAAI,YAAY;AACd,iBAAW,KAAK;AAChB,eAAS,EAAE;AACX,qBAAe,CAAC,CAAC;AACjB,yBAAmB,CAAC,CAAC;AAAA,IACvB,OACK;AACH,iBAAW,IAAI;AACf,iBAAW,MAAM;AAhOvB;AAiOQ,uBAAS,YAAT,mBAAkB;AAAA,MACpB,GAAG,GAAG;AAAA,IACR;AAAA,EACF;AAEA,SACM,qBAAC,WAEC;AAAA,yBAAC,SAAI,WAAW,2DACb;AAAA,OAAC,kBACA;AAAA,QAAC;AAAA;AAAA,UACC,SAAQ;AAAA,UACR,MAAK;AAAA,UACL,SAAS;AAAA,UACT,WAAU;AAAA,UAEV,8BAAC,UAAO;AAAA;AAAA,MACV;AAAA,MAED,kBACC,oBAAC,UAAO,WAAU,sBAAqB;AAAA,MAEzC;AAAA,QAAC;AAAA;AAAA,UACC,KAAK;AAAA,UACL,MAAK;AAAA,UACL,OAAO;AAAA,UACP,UAAU,OAAK,SAAS,EAAE,OAAO,KAAK;AAAA,UACtC,SAAS,MAAM,WAAW,IAAI;AAAA,UAC9B,WAAW,CAAC,MAAM;AA7PhC;AA8PgB,gBAAI,EAAE,QAAQ,SAAS;AACrB,6BAAS,YAAT,mBAAkB;AAAA,YACpB;AAAA,UACF;AAAA,UACA,aAAa,iBAAiB,EAAE,cAAc,IAAI,EAAE,cAAc;AAAA,UAClE,WAAW,8FACT,kBAAkB,aAAa,WAAW,SAC5C;AAAA;AAAA,MACF;AAAA,OACF;AAAA,IAEC,YAAY,WAAW,gBAAgB,SAAS,KAAK,YAAY,SAAS,MACzE,qBAAC,SAAI,KAAK,gBACR;AAAA,0BAAC,aAAU;AAAA,MACX,qBAAC,eACE;AAAA,SAAC,WAAW,gBAAgB,WAAW,KAAK,YAAY,WAAW,KAAK,MAAM,UAAU,KACvF,oBAAC,gBAAc,kCAAwB,EAAE,WAAW,IAAI,EAAE,aAAa,GAAE;AAAA,QAG1E,WACC,oBAAC,gBAAa,8BAAC,kBAAe,GAAE;AAAA,QAIjC,yBAAyB,CAAC,WAAW,gBAAgB,SAAS,KAC7D,oBAAC,gBAAa,SAAS,EAAE,gBAAgB,GACtC,0BAAgB,IAAI,CAAC,aAAa;AACjC,gBAAM,kBAAkB,kBAAkB,QAAQ;AAElD,iBACE;AAAA,YAAC;AAAA;AAAA,cAEC,UAAU,MAAM,uBAAuB,iBAAiB,YAAY;AAAA,cACpE,WAAU;AAAA,cAEV;AAAA,oCAAC,SAAI,WAAU,eAAe,mBAAS,cAAa;AAAA,gBACpD,oBAAC,SAAI,WAAU,iCACZ,mBAAS,iBACZ;AAAA;AAAA;AAAA,YAPK,YAAY,SAAS,EAAE;AAAA,UAQ9B;AAAA,QAEJ,CAAC,GACH;AAAA,QAID,yBAAyB,gBAAgB,SAAS,KAAK,YAAY,SAAS,KAC3E,oBAAC,oBAAiB;AAAA,QAInB,CAAC,WAAW,YAAY,SAAS,KAChC,oBAAC,gBAAa,SAAS,wBAAwB,EAAE,mBAAmB,IAAI,QACrE,sBAAY,IAAI,CAAC,YAAY;AAnTlD;AAoTsB,gBAAM,WAAW,cAAc,OAAO;AACtC,gBAAM,cAAY,aAAQ,eAAR,mBAAoB,QACjC,IAAG,aAAQ,eAAR,mBAAoB,IAAI,KAAI,mBAAQ,aAAR,mBAAkB,gBAAlB,mBAAgC,EAAE,KAAI,mBAAQ,aAAR,mBAAkB,gBAAlB,mBAAgC,EAAE;AAE5G,iBACE;AAAA,YAAC;AAAA;AAAA,cAEC,UAAU,MAAM,uBAAuB,OAAO;AAAA,cAC9C,WAAU;AAAA,cAEV;AAAA,oCAAC,SAAI,WAAU,eAAe,mBAAS,MAAK;AAAA,gBAC5C,oBAAC,SAAI,WAAU,iCAAiC,mBAAS,QAAO;AAAA;AAAA;AAAA,YAL3D;AAAA,UAMP;AAAA,QAEJ,CAAC,GACH;AAAA,SAEJ;AAAA,OACF;AAAA,KAEJ;AAER;","names":["address"]}
|
|
@@ -1,3 +1,8 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
|
-
|
|
2
|
+
import type { Organization } from '../../../../../types/dbTypes';
|
|
3
|
+
export declare const MapLayers: ({ minioBaseUrl, organization, maptilerKey }: {
|
|
4
|
+
minioBaseUrl?: string;
|
|
5
|
+
organization?: Organization;
|
|
6
|
+
maptilerKey?: string;
|
|
7
|
+
}) => React.JSX.Element;
|
|
3
8
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../../../src/core/components/viewers/map/src/MapLayers/index.tsx"],"names":[],"mappings":"AAKA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../../../src/core/components/viewers/map/src/MapLayers/index.tsx"],"names":[],"mappings":"AAKA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAU9B,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,8BAA8B,CAAA;AAahE,eAAO,MAAM,SAAS,GAAI,6CAA6C;IAAE,YAAY,CAAC,EAAE,MAAM,CAAC;IAAC,YAAY,CAAC,EAAE,YAAY,CAAC;IAAC,WAAW,CAAC,EAAE,MAAM,CAAA;CAAE,sBAYlJ,CAAA"}
|
|
@@ -17,13 +17,13 @@ function BimLayerGate() {
|
|
|
17
17
|
if (bimState.bim.bimModelsAddedToMap.length === 0) return null;
|
|
18
18
|
return /* @__PURE__ */ jsx(LazyBimLayer, {});
|
|
19
19
|
}
|
|
20
|
-
const MapLayers = () => {
|
|
20
|
+
const MapLayers = ({ minioBaseUrl, organization, maptilerKey }) => {
|
|
21
21
|
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
22
|
-
/* @__PURE__ */ jsx(CountryLayer, {}),
|
|
22
|
+
/* @__PURE__ */ jsx(CountryLayer, { organization, maptilerKey }),
|
|
23
23
|
/* @__PURE__ */ jsx(OpenDataLayers, {}),
|
|
24
24
|
/* @__PURE__ */ jsx(BuildingLayer, {}),
|
|
25
25
|
/* @__PURE__ */ jsx(CommentLayer, {}),
|
|
26
|
-
/* @__PURE__ */ jsx(SensorLayers, {}),
|
|
26
|
+
/* @__PURE__ */ jsx(SensorLayers, { minioBaseUrl }),
|
|
27
27
|
/* @__PURE__ */ jsx(FileLayers, {}),
|
|
28
28
|
/* @__PURE__ */ jsx(BimLayerGate, {})
|
|
29
29
|
] });
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../../src/core/components/viewers/map/src/MapLayers/index.tsx"],"sourcesContent":["// SPDX-License-Identifier: AGPL-3.0-or-later\r\n// Copyright (C) 2025 Collab Digital Twins\r\n\r\n'use client'\r\n\r\nimport * as React from 'react'\r\nimport dynamic from 'next/dynamic'\r\n\r\nimport { CountryLayer } from './src/CountryLayer'\r\nimport { CommentLayer } from './src/CommentLayer'\r\nimport { SensorLayers } from './src/SensorsLayer'\r\nimport { FileLayers } from './src/FileLayer'\r\nimport { OpenDataLayers } from './src/OpenDataLayer/src'\r\nimport { BuildingLayer } from './src/BuildingLayers'\r\nimport { useBimContext } from '../../../../../store'\r\n\r\nconst LazyBimLayer = dynamic(\r\n () => import('./src/BimLayer').then(m => ({ default: m.BimLayer })),\r\n { ssr: false },\r\n)\r\n\r\nfunction BimLayerGate() {\r\n const { state: bimState } = useBimContext()\r\n if (bimState.bim.bimModelsAddedToMap.length === 0) return null\r\n return <LazyBimLayer />\r\n}\r\n\r\nexport const MapLayers = () => {\r\n return (\r\n <>\r\n <CountryLayer />\r\n <OpenDataLayers />\r\n <BuildingLayer />\r\n <CommentLayer />\r\n <SensorLayers />\r\n <FileLayers />\r\n <BimLayerGate />\r\n </>\r\n )\r\n}\r\n"],"mappings":";
|
|
1
|
+
{"version":3,"sources":["../../../../../../../src/core/components/viewers/map/src/MapLayers/index.tsx"],"sourcesContent":["// SPDX-License-Identifier: AGPL-3.0-or-later\r\n// Copyright (C) 2025 Collab Digital Twins\r\n\r\n'use client'\r\n\r\nimport * as React from 'react'\r\nimport dynamic from 'next/dynamic'\r\n\r\nimport { CountryLayer } from './src/CountryLayer'\r\nimport { CommentLayer } from './src/CommentLayer'\r\nimport { SensorLayers } from './src/SensorsLayer'\r\nimport { FileLayers } from './src/FileLayer'\r\nimport { OpenDataLayers } from './src/OpenDataLayer/src'\r\nimport { BuildingLayer } from './src/BuildingLayers'\r\nimport { useBimContext } from '../../../../../store'\r\nimport type { Organization } from '../../../../../types/dbTypes'\r\n\r\nconst LazyBimLayer = dynamic(\r\n () => import('./src/BimLayer').then(m => ({ default: m.BimLayer })),\r\n { ssr: false },\r\n)\r\n\r\nfunction BimLayerGate() {\r\n const { state: bimState } = useBimContext()\r\n if (bimState.bim.bimModelsAddedToMap.length === 0) return null\r\n return <LazyBimLayer />\r\n}\r\n\r\nexport const MapLayers = ({ minioBaseUrl, organization, maptilerKey }: { minioBaseUrl?: string; organization?: Organization; maptilerKey?: string }) => {\r\n return (\r\n <>\r\n <CountryLayer organization={organization} maptilerKey={maptilerKey} />\r\n <OpenDataLayers />\r\n <BuildingLayer />\r\n <CommentLayer />\r\n <SensorLayers minioBaseUrl={minioBaseUrl} />\r\n <FileLayers />\r\n <BimLayerGate />\r\n </>\r\n )\r\n}\r\n"],"mappings":";AAyBS,SAKL,UALK,KAKL,YALK;AAnBT,OAAO,aAAa;AAEpB,SAAS,oBAAoB;AAC7B,SAAS,oBAAoB;AAC7B,SAAS,oBAAoB;AAC7B,SAAS,kBAAkB;AAC3B,SAAS,sBAAsB;AAC/B,SAAS,qBAAqB;AAC9B,SAAS,qBAAqB;AAG9B,MAAM,eAAe;AAAA,EACnB,MAAM,OAAO,gBAAgB,EAAE,KAAK,QAAM,EAAE,SAAS,EAAE,SAAS,EAAE;AAAA,EAClE,EAAE,KAAK,MAAM;AACf;AAEA,SAAS,eAAe;AACtB,QAAM,EAAE,OAAO,SAAS,IAAI,cAAc;AAC1C,MAAI,SAAS,IAAI,oBAAoB,WAAW,EAAG,QAAO;AAC1D,SAAO,oBAAC,gBAAa;AACvB;AAEO,MAAM,YAAY,CAAC,EAAE,cAAc,cAAc,YAAY,MAAoF;AACtJ,SACE,iCACE;AAAA,wBAAC,gBAAa,cAA4B,aAA0B;AAAA,IACpE,oBAAC,kBAAe;AAAA,IAChB,oBAAC,iBAAc;AAAA,IACf,oBAAC,gBAAa;AAAA,IACd,oBAAC,gBAAa,cAA4B;AAAA,IAC1C,oBAAC,cAAW;AAAA,IACZ,oBAAC,gBAAa;AAAA,KAChB;AAEJ;","names":[]}
|
|
@@ -1,3 +1,7 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
|
-
|
|
2
|
+
import type { Organization } from '../../../../../../../types/dbTypes';
|
|
3
|
+
export declare const CountryLayer: ({ organization, maptilerKey }: {
|
|
4
|
+
organization?: Organization;
|
|
5
|
+
maptilerKey?: string;
|
|
6
|
+
}) => React.JSX.Element;
|
|
3
7
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../../../../../src/core/components/viewers/map/src/MapLayers/src/CountryLayer/index.tsx"],"names":[],"mappings":"AAKA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../../../../../src/core/components/viewers/map/src/MapLayers/src/CountryLayer/index.tsx"],"names":[],"mappings":"AAKA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAM9B,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oCAAoC,CAAA;AAyItE,eAAO,MAAM,YAAY,GAAI,+BAA+B;IAAE,YAAY,CAAC,EAAE,YAAY,CAAC;IAAC,WAAW,CAAC,EAAE,MAAM,CAAA;CAAE,sBA0IhH,CAAA"}
|
|
@@ -22,13 +22,10 @@ import { jsx } from "react/jsx-runtime";
|
|
|
22
22
|
import * as React from "react";
|
|
23
23
|
import { Source, Layer } from "react-map-gl/maplibre";
|
|
24
24
|
import { MapContext } from "../../../../../../../store/Map/context";
|
|
25
|
-
import { useAppConfigContext } from "../../../../../../../store/AppConfig/context";
|
|
26
25
|
import { useSearchParams, useRouter } from "next/navigation";
|
|
27
26
|
import { hexToRgba, buildSubdivisionUrl } from "./countryLayerUtils";
|
|
28
27
|
const DEFAULT_BORDER_COLOR = "#73cee2";
|
|
29
28
|
const BOUNDARIES_SOURCE_ID = "openmaptiles-boundaries";
|
|
30
|
-
const MAPTILER_KEY = process.env.NEXT_PUBLIC_MAPTILER_KEY;
|
|
31
|
-
const BOUNDARIES_TILESET_URL = `https://api.maptiler.com/tiles/v3/tiles.json?key=${MAPTILER_KEY}`;
|
|
32
29
|
const GLOBAL_LAYER_IDS = [
|
|
33
30
|
"global-borders-country",
|
|
34
31
|
"global-borders-region",
|
|
@@ -36,12 +33,13 @@ const GLOBAL_LAYER_IDS = [
|
|
|
36
33
|
"global-labels-state",
|
|
37
34
|
"global-labels-city"
|
|
38
35
|
];
|
|
39
|
-
function addGlobalBorderLayer(map, color) {
|
|
36
|
+
function addGlobalBorderLayer(map, color, maptilerKey) {
|
|
37
|
+
if (!maptilerKey) return;
|
|
40
38
|
try {
|
|
41
39
|
if (!map.getSource(BOUNDARIES_SOURCE_ID)) {
|
|
42
40
|
map.addSource(BOUNDARIES_SOURCE_ID, {
|
|
43
41
|
type: "vector",
|
|
44
|
-
url:
|
|
42
|
+
url: `https://api.maptiler.com/tiles/v3/tiles.json?key=${maptilerKey}`
|
|
45
43
|
});
|
|
46
44
|
}
|
|
47
45
|
if (!map.getLayer("global-borders-country")) {
|
|
@@ -148,11 +146,9 @@ function addGlobalBorderLayer(map, color) {
|
|
|
148
146
|
} catch (e) {
|
|
149
147
|
}
|
|
150
148
|
}
|
|
151
|
-
const CountryLayer = () => {
|
|
149
|
+
const CountryLayer = ({ organization, maptilerKey }) => {
|
|
152
150
|
const { state: mapState, dispatch: mapDispatch } = React.useContext(MapContext);
|
|
153
151
|
const { map, currentLocation } = mapState.map;
|
|
154
|
-
const { state: appConfigState } = useAppConfigContext();
|
|
155
|
-
const organization = appConfigState.appConfig.organization;
|
|
156
152
|
const searchParams = useSearchParams();
|
|
157
153
|
const router = useRouter();
|
|
158
154
|
const debounceRef = React.useRef(null);
|
|
@@ -161,7 +157,7 @@ const CountryLayer = () => {
|
|
|
161
157
|
const subdivisionUrl = buildSubdivisionUrl(countryCode);
|
|
162
158
|
React.useEffect(() => {
|
|
163
159
|
if (!map) return;
|
|
164
|
-
const addNow = () => addGlobalBorderLayer(map, borderColor);
|
|
160
|
+
const addNow = () => addGlobalBorderLayer(map, borderColor, maptilerKey);
|
|
165
161
|
if (map.isStyleLoaded()) addNow();
|
|
166
162
|
map.on("styledata", addNow);
|
|
167
163
|
return () => {
|
|
@@ -173,7 +169,7 @@ const CountryLayer = () => {
|
|
|
173
169
|
} catch (e) {
|
|
174
170
|
}
|
|
175
171
|
};
|
|
176
|
-
}, [map, borderColor]);
|
|
172
|
+
}, [map, borderColor, maptilerKey]);
|
|
177
173
|
const orgHasLocation = !!((organization == null ? void 0 : organization.countrySubdivision) || (organization == null ? void 0 : organization.municipality));
|
|
178
174
|
React.useEffect(() => {
|
|
179
175
|
var _a, _b;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../../../../src/core/components/viewers/map/src/MapLayers/src/CountryLayer/index.tsx"],"sourcesContent":["\"use client\"\r\n\r\n// SPDX-License-Identifier: AGPL-3.0-or-later\r\n// Copyright (C) 2025 Collab Digital Twins\r\n\r\nimport * as React from 'react'\r\nimport { Source, Layer } from 'react-map-gl/maplibre'\r\nimport type { MapMouseEvent } from 'maplibre-gl'\r\nimport { MapContext } from '../../../../../../../store/Map/context'\r\nimport { useAppConfigContext } from '../../../../../../../store/AppConfig/context'\r\nimport { useSearchParams, useRouter } from 'next/navigation'\r\nimport { hexToRgba, buildSubdivisionUrl } from './countryLayerUtils'\r\n\r\nconst DEFAULT_BORDER_COLOR = '#73cee2'\r\n// satellite.json's openmaptiles source uses MapTiler's 'tiles/buildings' tileset which lacks the boundary + place layers. Pull both from the full openmaptiles v3 schema (same tileset blank.json uses).\r\nconst BOUNDARIES_SOURCE_ID = 'openmaptiles-boundaries'\r\n// Key is supplied by the consuming app via NEXT_PUBLIC_MAPTILER_KEY (Next inlines\r\n// it at build time). Kept out of source so @collabdt/core carries no secret.\r\nconst MAPTILER_KEY = process.env.NEXT_PUBLIC_MAPTILER_KEY\r\nconst BOUNDARIES_TILESET_URL = `https://api.maptiler.com/tiles/v3/tiles.json?key=${MAPTILER_KEY}`\r\nconst GLOBAL_LAYER_IDS = [\r\n 'global-borders-country',\r\n 'global-borders-region',\r\n 'global-labels-country',\r\n 'global-labels-state',\r\n 'global-labels-city',\r\n] as const\r\n\r\nfunction addGlobalBorderLayer(map: any, color: string) {\r\n try {\r\n if (!map.getSource(BOUNDARIES_SOURCE_ID)) {\r\n map.addSource(BOUNDARIES_SOURCE_ID, {\r\n type: 'vector',\r\n url: BOUNDARIES_TILESET_URL,\r\n })\r\n }\r\n\r\n // Country borders — visible at all zooms, slightly thinner up close where states take over.\r\n if (!map.getLayer('global-borders-country')) {\r\n map.addLayer({\r\n id: 'global-borders-country',\r\n type: 'line',\r\n source: BOUNDARIES_SOURCE_ID,\r\n 'source-layer': 'boundary',\r\n filter: ['all', ['==', 'admin_level', 2], ['==', 'maritime', 0]],\r\n layout: { 'line-cap': 'round', 'line-join': 'round' },\r\n paint: {\r\n 'line-color': color,\r\n 'line-width': { base: 1, stops: [[0, 0.8], [4, 1.4], [10, 1.4], [14, 1]] },\r\n },\r\n })\r\n }\r\n\r\n // State / province borders — fade in around zoom 3, dominate from 5+.\r\n if (!map.getLayer('global-borders-region')) {\r\n map.addLayer({\r\n id: 'global-borders-region',\r\n type: 'line',\r\n source: BOUNDARIES_SOURCE_ID,\r\n 'source-layer': 'boundary',\r\n filter: ['all', ['in', 'admin_level', 3, 4], ['==', 'maritime', 0]],\r\n minzoom: 2,\r\n layout: { 'line-cap': 'round', 'line-join': 'round' },\r\n paint: {\r\n 'line-color': color,\r\n 'line-width': { base: 1, stops: [[2, 0], [4, 0.5], [8, 1.4], [14, 2.4]] },\r\n 'line-opacity': { base: 1, stops: [[2, 0], [4, 0.7], [6, 1]] },\r\n },\r\n })\r\n }\r\n\r\n // Country labels — small/global up to zoom ~6, gone by 8.\r\n if (!map.getLayer('global-labels-country')) {\r\n map.addLayer({\r\n id: 'global-labels-country',\r\n type: 'symbol',\r\n source: BOUNDARIES_SOURCE_ID,\r\n 'source-layer': 'place',\r\n filter: ['==', 'class', 'country'],\r\n minzoom: 1,\r\n maxzoom: 8,\r\n layout: {\r\n 'text-field': ['coalesce', ['get', 'name:latin'], ['get', 'name']],\r\n 'text-font': ['Noto Sans Regular'],\r\n 'text-size': { base: 1, stops: [[1, 10], [4, 13], [6, 16]] },\r\n 'text-letter-spacing': 0.1,\r\n 'text-max-width': 8,\r\n 'text-transform': 'uppercase',\r\n },\r\n paint: {\r\n 'text-color': '#ffffff',\r\n 'text-halo-color': 'rgba(0,0,0,0.7)',\r\n 'text-halo-width': 1.2,\r\n 'text-opacity': { base: 1, stops: [[5, 1], [7, 0]] },\r\n },\r\n })\r\n }\r\n\r\n // State / province labels — cover zoom 3–8.\r\n if (!map.getLayer('global-labels-state')) {\r\n map.addLayer({\r\n id: 'global-labels-state',\r\n type: 'symbol',\r\n source: BOUNDARIES_SOURCE_ID,\r\n 'source-layer': 'place',\r\n filter: ['in', 'class', 'state', 'province'],\r\n minzoom: 3,\r\n maxzoom: 9,\r\n layout: {\r\n 'text-field': ['coalesce', ['get', 'name:latin'], ['get', 'name']],\r\n 'text-font': ['Noto Sans Regular'],\r\n 'text-size': { base: 1, stops: [[3, 9], [6, 12], [8, 14]] },\r\n 'text-letter-spacing': 0.05,\r\n 'text-max-width': 8,\r\n },\r\n paint: {\r\n 'text-color': '#e8e8e8',\r\n 'text-halo-color': 'rgba(0,0,0,0.6)',\r\n 'text-halo-width': 1,\r\n 'text-opacity': { base: 1, stops: [[3, 0], [4, 1], [8, 1], [9, 0]] },\r\n },\r\n })\r\n }\r\n\r\n // Major city labels (rank ≤ 4 = most prominent cities) — appear when zoomed in past country level.\r\n if (!map.getLayer('global-labels-city')) {\r\n map.addLayer({\r\n id: 'global-labels-city',\r\n type: 'symbol',\r\n source: BOUNDARIES_SOURCE_ID,\r\n 'source-layer': 'place',\r\n filter: ['in', 'class', 'city', 'town'],\r\n minzoom: 5,\r\n maxzoom: 13,\r\n layout: {\r\n 'text-field': ['coalesce', ['get', 'name:latin'], ['get', 'name']],\r\n 'text-font': ['Noto Sans Regular'],\r\n 'text-size': { base: 1, stops: [[5, 10], [9, 13], [12, 15]] },\r\n 'text-max-width': 8,\r\n },\r\n paint: {\r\n 'text-color': '#dcdcdc',\r\n 'text-halo-color': 'rgba(0,0,0,0.7)',\r\n 'text-halo-width': 1.1,\r\n },\r\n })\r\n }\r\n } catch {\r\n // style may be in a transitional state\r\n }\r\n}\r\n\r\nexport const CountryLayer = () => {\r\n const { state: mapState, dispatch: mapDispatch } = React.useContext(MapContext)\r\n const { map, currentLocation } = mapState.map\r\n const { state: appConfigState } = useAppConfigContext()\r\n const organization = appConfigState.appConfig.organization\r\n const searchParams = useSearchParams()\r\n const router = useRouter()\r\n const debounceRef = React.useRef<NodeJS.Timeout | null>(null)\r\n\r\n const countryCode = (organization?.country || 'CA').toUpperCase()\r\n const borderColor = hexToRgba((organization?.mainColor as string) || DEFAULT_BORDER_COLOR, 0.7)\r\n const subdivisionUrl = buildSubdivisionUrl(countryCode)\r\n\r\n // Add the global boundary line layer imperatively (vector-tile lines from the style's openmaptiles source). Re-adds on style swap so it survives basemap changes.\r\n React.useEffect(() => {\r\n if (!map) return\r\n const addNow = () => addGlobalBorderLayer(map, borderColor)\r\n if (map.isStyleLoaded()) addNow()\r\n map.on('styledata', addNow)\r\n return () => {\r\n try {\r\n map.off('styledata', addNow)\r\n for (const id of GLOBAL_LAYER_IDS) {\r\n if (map.getLayer(id)) map.removeLayer(id)\r\n }\r\n } catch {\r\n // map may be destroyed\r\n }\r\n }\r\n }, [map, borderColor])\r\n\r\n // If the org already has a fixed subdivision or municipality, lock them in and skip hover\r\n const orgHasLocation = !!(organization?.countrySubdivision || organization?.municipality)\r\n\r\n // When org has fixed location values, push them into currentLocation once\r\n React.useEffect(() => {\r\n if (!orgHasLocation) return\r\n mapDispatch({\r\n type: 'UPDATE_LOCATION',\r\n payload: {\r\n currentLocation: {\r\n ...(currentLocation ?? {}),\r\n countrySubdivision: organization?.countrySubdivision ?? currentLocation?.countrySubdivision,\r\n municipality: organization?.municipality ?? currentLocation?.municipality,\r\n } as any,\r\n },\r\n })\r\n // eslint-disable-next-line react-hooks/exhaustive-deps\r\n }, [orgHasLocation])\r\n\r\n React.useEffect(() => {\r\n if (orgHasLocation) return\r\n if (!(map && currentLocation)) return\r\n\r\n const currentCountrySubdivision = currentLocation.countrySubdivision || null\r\n const currentMunicipality = currentLocation.municipality || null\r\n\r\n function onMouseMove(e: MapMouseEvent) {\r\n if (!map.isStyleLoaded()) return\r\n\r\n if (debounceRef.current) clearTimeout(debounceRef.current)\r\n\r\n const eventPoint = e.point\r\n\r\n debounceRef.current = setTimeout(() => {\r\n const features = map.queryRenderedFeatures(eventPoint, {\r\n layers: ['admin-subdivisions-fill'],\r\n })\r\n\r\n // if (features.length > 0) {\r\n // console.group('[CountryLayer] hover')\r\n // features.forEach(f => console.log(f.properties))\r\n // console.groupEnd()\r\n // }\r\n\r\n let newCountrySubdivision = null\r\n\r\n if (features.length > 0) {\r\n const f = features[0]\r\n const isoCC = f.properties?.ISO_CC\r\n const isoCode = f.properties?.ISO_CODE\r\n // Build \"CA-ON\" from ISO_CC \"CA\" + ISO_CODE \"CAON\" → strip prefix to get \"ON\"\r\n if (isoCC && isoCode && isoCode.startsWith(isoCC)) {\r\n newCountrySubdivision = `${isoCC}-${isoCode.slice(isoCC.length)}`\r\n } else {\r\n newCountrySubdivision = (isoCode || f.properties?.NAME || null) as string | null\r\n }\r\n }\r\n\r\n const finalCountrySubdivision = newCountrySubdivision || currentCountrySubdivision\r\n\r\n if (finalCountrySubdivision !== currentCountrySubdivision) {\r\n mapDispatch({\r\n type: 'UPDATE_LOCATION',\r\n payload: {\r\n currentLocation: {\r\n ...currentLocation,\r\n countrySubdivision: finalCountrySubdivision,\r\n municipality: currentMunicipality,\r\n },\r\n },\r\n })\r\n\r\n const params = new URLSearchParams(searchParams.toString())\r\n params.set('countrySubdivision', finalCountrySubdivision || '')\r\n router.replace(`?${params.toString()}`, { scroll: false })\r\n }\r\n }, 300)\r\n }\r\n\r\n if (map.isStyleLoaded()) {\r\n map.on('mousemove', onMouseMove)\r\n } else {\r\n map.once('styledata', () => {\r\n map.on('mousemove', onMouseMove)\r\n })\r\n }\r\n\r\n return () => {\r\n map.off('mousemove', onMouseMove)\r\n if (debounceRef.current) clearTimeout(debounceRef.current)\r\n }\r\n }, [map, mapDispatch, currentLocation, searchParams, router])\r\n\r\n if (orgHasLocation) return null\r\n\r\n // Invisible ArcGIS subdivision polygons for hover hit-detection (only mounted when the org hasn't locked a subdivision). The visible borders come from the imperative openmaptiles layer above.\r\n return (\r\n <Source\r\n id=\"admin-subdivisions-source\"\r\n type=\"geojson\"\r\n data={subdivisionUrl}\r\n >\r\n <Layer\r\n id=\"admin-subdivisions-fill\"\r\n type=\"fill\"\r\n paint={{ 'fill-color': 'transparent', 'fill-opacity': 0 }}\r\n />\r\n </Source>\r\n )\r\n}\r\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../../../../../../../../src/core/components/viewers/map/src/MapLayers/src/CountryLayer/index.tsx"],"sourcesContent":["\"use client\"\r\n\r\n// SPDX-License-Identifier: AGPL-3.0-or-later\r\n// Copyright (C) 2025 Collab Digital Twins\r\n\r\nimport * as React from 'react'\r\nimport { Source, Layer } from 'react-map-gl/maplibre'\r\nimport type { MapMouseEvent } from 'maplibre-gl'\r\nimport { MapContext } from '../../../../../../../store/Map/context'\r\nimport { useSearchParams, useRouter } from 'next/navigation'\r\nimport { hexToRgba, buildSubdivisionUrl } from './countryLayerUtils'\r\nimport type { Organization } from '../../../../../../../types/dbTypes'\r\n\r\nconst DEFAULT_BORDER_COLOR = '#73cee2'\r\nconst BOUNDARIES_SOURCE_ID = 'openmaptiles-boundaries'\r\nconst GLOBAL_LAYER_IDS = [\r\n 'global-borders-country',\r\n 'global-borders-region',\r\n 'global-labels-country',\r\n 'global-labels-state',\r\n 'global-labels-city',\r\n] as const\r\n\r\nfunction addGlobalBorderLayer(map: any, color: string, maptilerKey?: string) {\r\n if (!maptilerKey) return\r\n try {\r\n if (!map.getSource(BOUNDARIES_SOURCE_ID)) {\r\n map.addSource(BOUNDARIES_SOURCE_ID, {\r\n type: 'vector',\r\n url: `https://api.maptiler.com/tiles/v3/tiles.json?key=${maptilerKey}`,\r\n })\r\n }\r\n\r\n // Country borders — visible at all zooms, slightly thinner up close where states take over.\r\n if (!map.getLayer('global-borders-country')) {\r\n map.addLayer({\r\n id: 'global-borders-country',\r\n type: 'line',\r\n source: BOUNDARIES_SOURCE_ID,\r\n 'source-layer': 'boundary',\r\n filter: ['all', ['==', 'admin_level', 2], ['==', 'maritime', 0]],\r\n layout: { 'line-cap': 'round', 'line-join': 'round' },\r\n paint: {\r\n 'line-color': color,\r\n 'line-width': { base: 1, stops: [[0, 0.8], [4, 1.4], [10, 1.4], [14, 1]] },\r\n },\r\n })\r\n }\r\n\r\n // State / province borders — fade in around zoom 3, dominate from 5+.\r\n if (!map.getLayer('global-borders-region')) {\r\n map.addLayer({\r\n id: 'global-borders-region',\r\n type: 'line',\r\n source: BOUNDARIES_SOURCE_ID,\r\n 'source-layer': 'boundary',\r\n filter: ['all', ['in', 'admin_level', 3, 4], ['==', 'maritime', 0]],\r\n minzoom: 2,\r\n layout: { 'line-cap': 'round', 'line-join': 'round' },\r\n paint: {\r\n 'line-color': color,\r\n 'line-width': { base: 1, stops: [[2, 0], [4, 0.5], [8, 1.4], [14, 2.4]] },\r\n 'line-opacity': { base: 1, stops: [[2, 0], [4, 0.7], [6, 1]] },\r\n },\r\n })\r\n }\r\n\r\n // Country labels — small/global up to zoom ~6, gone by 8.\r\n if (!map.getLayer('global-labels-country')) {\r\n map.addLayer({\r\n id: 'global-labels-country',\r\n type: 'symbol',\r\n source: BOUNDARIES_SOURCE_ID,\r\n 'source-layer': 'place',\r\n filter: ['==', 'class', 'country'],\r\n minzoom: 1,\r\n maxzoom: 8,\r\n layout: {\r\n 'text-field': ['coalesce', ['get', 'name:latin'], ['get', 'name']],\r\n 'text-font': ['Noto Sans Regular'],\r\n 'text-size': { base: 1, stops: [[1, 10], [4, 13], [6, 16]] },\r\n 'text-letter-spacing': 0.1,\r\n 'text-max-width': 8,\r\n 'text-transform': 'uppercase',\r\n },\r\n paint: {\r\n 'text-color': '#ffffff',\r\n 'text-halo-color': 'rgba(0,0,0,0.7)',\r\n 'text-halo-width': 1.2,\r\n 'text-opacity': { base: 1, stops: [[5, 1], [7, 0]] },\r\n },\r\n })\r\n }\r\n\r\n // State / province labels — cover zoom 3–8.\r\n if (!map.getLayer('global-labels-state')) {\r\n map.addLayer({\r\n id: 'global-labels-state',\r\n type: 'symbol',\r\n source: BOUNDARIES_SOURCE_ID,\r\n 'source-layer': 'place',\r\n filter: ['in', 'class', 'state', 'province'],\r\n minzoom: 3,\r\n maxzoom: 9,\r\n layout: {\r\n 'text-field': ['coalesce', ['get', 'name:latin'], ['get', 'name']],\r\n 'text-font': ['Noto Sans Regular'],\r\n 'text-size': { base: 1, stops: [[3, 9], [6, 12], [8, 14]] },\r\n 'text-letter-spacing': 0.05,\r\n 'text-max-width': 8,\r\n },\r\n paint: {\r\n 'text-color': '#e8e8e8',\r\n 'text-halo-color': 'rgba(0,0,0,0.6)',\r\n 'text-halo-width': 1,\r\n 'text-opacity': { base: 1, stops: [[3, 0], [4, 1], [8, 1], [9, 0]] },\r\n },\r\n })\r\n }\r\n\r\n // Major city labels (rank ≤ 4 = most prominent cities) — appear when zoomed in past country level.\r\n if (!map.getLayer('global-labels-city')) {\r\n map.addLayer({\r\n id: 'global-labels-city',\r\n type: 'symbol',\r\n source: BOUNDARIES_SOURCE_ID,\r\n 'source-layer': 'place',\r\n filter: ['in', 'class', 'city', 'town'],\r\n minzoom: 5,\r\n maxzoom: 13,\r\n layout: {\r\n 'text-field': ['coalesce', ['get', 'name:latin'], ['get', 'name']],\r\n 'text-font': ['Noto Sans Regular'],\r\n 'text-size': { base: 1, stops: [[5, 10], [9, 13], [12, 15]] },\r\n 'text-max-width': 8,\r\n },\r\n paint: {\r\n 'text-color': '#dcdcdc',\r\n 'text-halo-color': 'rgba(0,0,0,0.7)',\r\n 'text-halo-width': 1.1,\r\n },\r\n })\r\n }\r\n } catch {\r\n // style may be in a transitional state\r\n }\r\n}\r\n\r\nexport const CountryLayer = ({ organization, maptilerKey }: { organization?: Organization; maptilerKey?: string }) => {\r\n const { state: mapState, dispatch: mapDispatch } = React.useContext(MapContext)\r\n const { map, currentLocation } = mapState.map\r\n const searchParams = useSearchParams()\r\n const router = useRouter()\r\n const debounceRef = React.useRef<NodeJS.Timeout | null>(null)\r\n\r\n const countryCode = (organization?.country || 'CA').toUpperCase()\r\n const borderColor = hexToRgba((organization?.mainColor as string) || DEFAULT_BORDER_COLOR, 0.7)\r\n const subdivisionUrl = buildSubdivisionUrl(countryCode)\r\n\r\n // Add the global boundary line layer imperatively (vector-tile lines from the style's openmaptiles source). Re-adds on style swap so it survives basemap changes.\r\n React.useEffect(() => {\r\n if (!map) return\r\n const addNow = () => addGlobalBorderLayer(map, borderColor, maptilerKey)\r\n if (map.isStyleLoaded()) addNow()\r\n map.on('styledata', addNow)\r\n return () => {\r\n try {\r\n map.off('styledata', addNow)\r\n for (const id of GLOBAL_LAYER_IDS) {\r\n if (map.getLayer(id)) map.removeLayer(id)\r\n }\r\n } catch {\r\n // map may be destroyed\r\n }\r\n }\r\n }, [map, borderColor, maptilerKey])\r\n\r\n // If the org already has a fixed subdivision or municipality, lock them in and skip hover\r\n const orgHasLocation = !!(organization?.countrySubdivision || organization?.municipality)\r\n\r\n // When org has fixed location values, push them into currentLocation once\r\n React.useEffect(() => {\r\n if (!orgHasLocation) return\r\n mapDispatch({\r\n type: 'UPDATE_LOCATION',\r\n payload: {\r\n currentLocation: {\r\n ...(currentLocation ?? {}),\r\n countrySubdivision: organization?.countrySubdivision ?? currentLocation?.countrySubdivision,\r\n municipality: organization?.municipality ?? currentLocation?.municipality,\r\n } as any,\r\n },\r\n })\r\n // eslint-disable-next-line react-hooks/exhaustive-deps\r\n }, [orgHasLocation])\r\n\r\n React.useEffect(() => {\r\n if (orgHasLocation) return\r\n if (!(map && currentLocation)) return\r\n\r\n const currentCountrySubdivision = currentLocation.countrySubdivision || null\r\n const currentMunicipality = currentLocation.municipality || null\r\n\r\n function onMouseMove(e: MapMouseEvent) {\r\n if (!map.isStyleLoaded()) return\r\n\r\n if (debounceRef.current) clearTimeout(debounceRef.current)\r\n\r\n const eventPoint = e.point\r\n\r\n debounceRef.current = setTimeout(() => {\r\n const features = map.queryRenderedFeatures(eventPoint, {\r\n layers: ['admin-subdivisions-fill'],\r\n })\r\n\r\n // if (features.length > 0) {\r\n // console.group('[CountryLayer] hover')\r\n // features.forEach(f => console.log(f.properties))\r\n // console.groupEnd()\r\n // }\r\n\r\n let newCountrySubdivision = null\r\n\r\n if (features.length > 0) {\r\n const f = features[0]\r\n const isoCC = f.properties?.ISO_CC\r\n const isoCode = f.properties?.ISO_CODE\r\n // Build \"CA-ON\" from ISO_CC \"CA\" + ISO_CODE \"CAON\" → strip prefix to get \"ON\"\r\n if (isoCC && isoCode && isoCode.startsWith(isoCC)) {\r\n newCountrySubdivision = `${isoCC}-${isoCode.slice(isoCC.length)}`\r\n } else {\r\n newCountrySubdivision = (isoCode || f.properties?.NAME || null) as string | null\r\n }\r\n }\r\n\r\n const finalCountrySubdivision = newCountrySubdivision || currentCountrySubdivision\r\n\r\n if (finalCountrySubdivision !== currentCountrySubdivision) {\r\n mapDispatch({\r\n type: 'UPDATE_LOCATION',\r\n payload: {\r\n currentLocation: {\r\n ...currentLocation,\r\n countrySubdivision: finalCountrySubdivision,\r\n municipality: currentMunicipality,\r\n },\r\n },\r\n })\r\n\r\n const params = new URLSearchParams(searchParams.toString())\r\n params.set('countrySubdivision', finalCountrySubdivision || '')\r\n router.replace(`?${params.toString()}`, { scroll: false })\r\n }\r\n }, 300)\r\n }\r\n\r\n if (map.isStyleLoaded()) {\r\n map.on('mousemove', onMouseMove)\r\n } else {\r\n map.once('styledata', () => {\r\n map.on('mousemove', onMouseMove)\r\n })\r\n }\r\n\r\n return () => {\r\n map.off('mousemove', onMouseMove)\r\n if (debounceRef.current) clearTimeout(debounceRef.current)\r\n }\r\n }, [map, mapDispatch, currentLocation, searchParams, router])\r\n\r\n if (orgHasLocation) return null\r\n\r\n // Invisible ArcGIS subdivision polygons for hover hit-detection (only mounted when the org hasn't locked a subdivision). The visible borders come from the imperative openmaptiles layer above.\r\n return (\r\n <Source\r\n id=\"admin-subdivisions-source\"\r\n type=\"geojson\"\r\n data={subdivisionUrl}\r\n >\r\n <Layer\r\n id=\"admin-subdivisions-fill\"\r\n type=\"fill\"\r\n paint={{ 'fill-color': 'transparent', 'fill-opacity': 0 }}\r\n />\r\n </Source>\r\n )\r\n}\r\n"],"mappings":"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","names":[]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../../../../../src/core/components/viewers/map/src/MapLayers/src/SensorsLayer/index.tsx"],"names":[],"mappings":"AAMA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAoD9B,eAAO,MAAM,YAAY,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../../../../../src/core/components/viewers/map/src/MapLayers/src/SensorsLayer/index.tsx"],"names":[],"mappings":"AAMA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAoD9B,eAAO,MAAM,YAAY,GAAI,kBAAkB;IAAE,YAAY,CAAC,EAAE,MAAM,CAAA;CAAE,sBA4TvE,CAAA"}
|
|
@@ -67,7 +67,7 @@ const SensorIconMarker = ({ feature, isHighlighted, onMouseEnter, onMouseLeave,
|
|
|
67
67
|
}
|
|
68
68
|
) }, String((_b = (_a = feature.properties) == null ? void 0 : _a.id) != null ? _b : `${coords.lng},${coords.lat}`));
|
|
69
69
|
};
|
|
70
|
-
const SensorLayers = () => {
|
|
70
|
+
const SensorLayers = ({ minioBaseUrl }) => {
|
|
71
71
|
var _a, _b;
|
|
72
72
|
const [hoveredSensorId, setHoveredSensorId] = React.useState(null);
|
|
73
73
|
const clusterLayer = createClusterLayer("sensors");
|
|
@@ -232,7 +232,7 @@ const SensorLayers = () => {
|
|
|
232
232
|
if (!popupInfo) return null;
|
|
233
233
|
const sensorType = sensorTypes.find((t2) => t2.id === popupInfo.typeId);
|
|
234
234
|
const liveSensor = sensors.find((s) => s.id === popupInfo.id);
|
|
235
|
-
const dataUrl = popupInfo.url ? `${
|
|
235
|
+
const dataUrl = popupInfo.url ? `${minioBaseUrl != null ? minioBaseUrl : ""}/sensors/${popupInfo.url}` : popupInfo.data || "";
|
|
236
236
|
return /* @__PURE__ */ jsxs(
|
|
237
237
|
Popup,
|
|
238
238
|
{
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../../../../src/core/components/viewers/map/src/MapLayers/src/SensorsLayer/index.tsx"],"sourcesContent":["'use client'\r\n\r\n// SPDX-License-Identifier: AGPL-3.0-or-later\r\n// Copyright (C) 2025 Collab Digital Twins\r\n\r\nimport { MapContext, MenusContext } from '../../../../../../../store'\r\nimport * as React from 'react'\r\nimport { Source, Layer, Popup, Marker } from 'react-map-gl/maplibre'\r\nimport type { MapGeoJSONFeature, MapLayerMouseEvent } from 'maplibre-gl'\r\nimport Sensor from '../../../../../../ui/Sensors/Sensor'\r\nimport { SensorType, ViewerNames, type Sensor as ISensor} from '../../../../../../../types/dbTypes'\r\n\r\nimport { MapLayerClickPriority, ClickCallback } from '../../../../utils/MapEventManager/MapClickManager'\r\nimport { extractCoordinatesFromFeature } from '../../../../utils/extractCoordinates'\r\nimport { useSensor, useSensors } from '../../../../../../../hooks/sensors/sensors'\r\nimport { useUsers } from '../../../../../../../hooks/users/users'\r\nimport { toast } from 'sonner'\r\nimport { useTranslations } from 'next-intl'\r\nimport { useSession } from 'next-auth/react'\r\nimport { createClusterLayer, createClusterCountLayer, createUnclusteredPointLayer } from '../mapLayersUtils'\r\nimport { useSensorTypes } from '../../../../../../../hooks/sensorTypes/sensorTypes'\r\nimport * as LR from 'lucide-react'\r\nimport { UNTAGGED_TAG } from '../../../../../../ui/Sensors/SensorsSection'\r\n\r\nconst SensorIconMarker = ({ feature, isHighlighted, onMouseEnter, onMouseLeave, sensorTypes }: { feature: MapGeoJSONFeature; isHighlighted?: boolean; onMouseEnter?: () => void; onMouseLeave?: () => void; sensorTypes: SensorType[] }) => {\r\n\r\n const coords = extractCoordinatesFromFeature(feature)\r\n if (!coords) return null\r\n\r\n const sensorType = sensorTypes.find(t => t.id === feature.properties?.typeId)\r\n const icon = sensorType?.icon || 'Radio'\r\n const SensorIcon = LR[icon] || LR.Radio\r\n\r\n return (\r\n <Marker key={String(feature.properties?.id ?? `${coords.lng},${coords.lat}`)} longitude={coords.lng} latitude={coords.lat} anchor=\"center\">\r\n <div\r\n style={{\r\n width: '36px',\r\n height: '36px',\r\n borderRadius: '50%',\r\n border: isHighlighted ? '3px solid #73cee2' : '2px solid white',\r\n backgroundColor: 'black',\r\n display: 'flex',\r\n alignItems: 'center',\r\n justifyContent: 'center',\r\n boxShadow: isHighlighted ? '0 0 12px rgba(115, 206, 226, 0.5)' : 'none',\r\n transition: 'all 0.2s ease-in-out',\r\n transform: isHighlighted ? 'scale(1.2)' : 'scale(1)',\r\n }}\r\n onMouseEnter={onMouseEnter}\r\n onMouseLeave={onMouseLeave}\r\n >\r\n <SensorIcon style={{ width: '20px', height: '20px', color: 'hsl(var(--primary-foreground))' }} />\r\n </div>\r\n </Marker>\r\n )\r\n}\r\n\r\nexport const SensorLayers = () => {\r\n const [hoveredSensorId, setHoveredSensorId] = React.useState<number | null>(null)\r\n const clusterLayer = createClusterLayer('sensors')\r\n const clusterCountLayer = createClusterCountLayer('sensors')\r\n const unclusteredPointLayer = createUnclusteredPointLayer('sensors')\r\n\r\n // global map state\r\n\r\n const t = useTranslations('SensorLayers')\r\n const tSensors = useTranslations('SensorsSection')\r\n\r\n const { state: mapState } = React.useContext(MapContext)\r\n const { map, mapClickManager } = mapState.map\r\n const { sensors } = useSensors()\r\n const { users } = useUsers()\r\n const user = useSession().data?.user\r\n\r\n const [popupInfo, setPopUpInfo] = React.useState<Partial<ISensor & { sensorType: SensorType }> | null>(null)\r\n const { deleteSensor, updateSensor } = useSensor(popupInfo?.id ?? null)\r\n const { state: menusState } = React.useContext(MenusContext)\r\n const { visibleSensorTypes, visibleSensorTags, currentSensorId } = menusState.menus\r\n const typesVisible = visibleSensorTypes?.[ViewerNames.map] || []\r\n const tagsVisible = visibleSensorTags?.[ViewerNames.map] || []\r\n\r\n const {sensorTypes} = useSensorTypes()\r\n\r\n const eligibleSensors: Array<ISensor & { authorName: string } & { sensorType: SensorType }> = sensors\r\n .filter((sensor) => sensor.viewer === ViewerNames.map)\r\n .filter((sensor) => {\r\n const matchesType = typesVisible.includes(sensor.typeId)\r\n const hasNoTags = !sensor.tags?.length\r\n const matchesTag = hasNoTags\r\n ? tagsVisible.includes(UNTAGGED_TAG)\r\n : sensor.tags?.some(tag => tagsVisible.includes(tag)) ?? false\r\n return matchesType || matchesTag\r\n })\r\n .map((sensor) => {\r\n const user = users.find(u => u.id === sensor.authorId)\r\n const sensorType = sensorTypes.find(t => t.id === sensor.typeId)\r\n return {\r\n ...sensor,\r\n authorName: user?.name ?? 'Unknown User',\r\n sensorType,\r\n }\r\n })\r\n\r\n // Close popup if the sensor was deleted\r\n React.useEffect(() => {\r\n if (popupInfo && !sensors.find((s) => s.id === popupInfo.id)) {\r\n setPopUpInfo(null)\r\n }\r\n }, [sensors, popupInfo])\r\n\r\n const geojsonSensorData = React.useMemo(() => {\r\n const convertDataToGeojson = (sensorData: typeof eligibleSensors): GeoJSON.FeatureCollection<GeoJSON.Point, { [key: string]: any }> => {\r\n const sensorFeatures: GeoJSON.Feature<GeoJSON.Point, { [key: string]: any }>[] = sensorData\r\n .map((sensor) => {\r\n const { longitude, latitude, id, name, typeId, sensorType, data, dataFormat, updateFrequency, createdAt, authorId, organizationId, visible, url, authorName } = sensor\r\n\r\n return {\r\n type: 'Feature',\r\n geometry: {\r\n type: 'Point',\r\n coordinates: [longitude, latitude],\r\n },\r\n properties: {\r\n id: Number(id),\r\n organizationId,\r\n visible,\r\n longitude,\r\n latitude,\r\n authorId: Number(authorId),\r\n authorName,\r\n name,\r\n typeId: Number(typeId),\r\n typeIcon: sensorType?.icon,\r\n data,\r\n dataFormat,\r\n updateFrequency,\r\n url,\r\n createdAt,\r\n viewer: ViewerNames.map,\r\n },\r\n }\r\n })\r\n\r\n \r\n\r\n const sensorFC: GeoJSON.FeatureCollection<GeoJSON.Point, { [key: string]: any }> = {\r\n type: 'FeatureCollection',\r\n features: sensorFeatures,\r\n }\r\n return sensorFC\r\n }\r\n return convertDataToGeojson(eligibleSensors)\r\n }, [eligibleSensors])\r\n\r\n // event listeners for sensor unclustered points\r\n React.useEffect(() => {\r\n if (!map) return\r\n const showSensorPopUp: ClickCallback = (e: MapLayerMouseEvent, features: MapGeoJSONFeature[]) => {\r\n const feature = features?.[0]\r\n\r\n if (!feature || feature.properties.point_count) return\r\n\r\n if (feature.geometry.type != 'Point') return\r\n const [longitude, latitude] = feature.geometry.coordinates\r\n const { id, authorId, name, typeId, data, dataFormat, updateFrequency, createdAt, url } = feature.properties\r\n setPopUpInfo({\r\n id: Number(id),\r\n authorId: Number(authorId),\r\n organizationId: feature.properties?.organizationId,\r\n visible: feature.properties?.visible,\r\n longitude,\r\n latitude,\r\n name,\r\n typeId: Number(typeId),\r\n data,\r\n dataFormat,\r\n updateFrequency,\r\n url,\r\n createdAt,\r\n viewer: ViewerNames.map,\r\n })\r\n }\r\n\r\n const mouseEnterChangeCursor = () => {\r\n map.getCanvas().style.cursor = 'pointer'\r\n }\r\n const mouseLeaveChangeCursor = () => {\r\n map.getCanvas().style.cursor = ''\r\n }\r\n\r\n // event listener for clicking on single point to show sensor, hover to change cursor\r\n mapClickManager.register('sensors-unclustered-points', MapLayerClickPriority.CommentLayersClickPriority, showSensorPopUp)\r\n\r\n map.on('mouseenter', 'sensors-unclustered-points', mouseEnterChangeCursor)\r\n map.on('mouseleave', 'sensors-unclustered-points', mouseLeaveChangeCursor)\r\n return () => {\r\n mapClickManager.unregister('sensors-unclustered-points')\r\n map.off('mouseenter', 'sensors-unclustered-points', mouseEnterChangeCursor)\r\n map.off('mouseleave', 'sensors-unclustered-points', mouseLeaveChangeCursor)\r\n }\r\n }, [map, mapClickManager])\r\n\r\n const handleRemoveSensor = () => {\r\n toast.success(t('sensorDeleted'))\r\n deleteSensor()\r\n setPopUpInfo(null)\r\n }\r\n\r\n // event listeners for clustered points\r\n React.useEffect(() => {\r\n if (!map) return\r\n\r\n const zoomInToDecluster = (e: MapLayerMouseEvent) => {\r\n const feature = e.features?.[0] // only proceed if this is actually a cluster\r\n if (!feature || !feature.properties.cluster_id) return\r\n\r\n const clusterId = feature.properties.cluster_id as number\r\n\r\n // Check if geometry has coordinates (not a GeometryCollection)\r\n if (!('coordinates' in feature.geometry)) return\r\n const [lng, lat] = feature.geometry.coordinates as [number, number]\r\n\r\n const source = map.getSource('sensors') as any\r\n console.log(source)\r\n console.log(clusterId)\r\n source.getClusterExpansionZoom(clusterId).then(\r\n (zoom: number) => {\r\n console.log('easing to')\r\n map.easeTo({ center: [lng, lat], zoom })\r\n },\r\n )\r\n .catch((error) => {\r\n console.error(error)\r\n })\r\n }\r\n\r\n const mouseEnterChangeCursor = () => { map.getCanvas().style.cursor = 'pointer' }\r\n const mouseLeaveChangeCursor = () => { map.getCanvas().style.cursor = '' }\r\n\r\n // event listener for clicking on single point to zoom in and decluster, hover to change cursor\r\n map.on('click', 'sensors-clusters', zoomInToDecluster)\r\n map.on('mouseenter', 'sensors-clusters', mouseEnterChangeCursor)\r\n map.on('mouseleave', 'sensors-clusters', mouseLeaveChangeCursor)\r\n return () => {\r\n map.off('click', 'sensors-clusters', zoomInToDecluster)\r\n map.off('mouseenter', 'sensors-clusters', mouseEnterChangeCursor)\r\n map.off('mouseleave', 'sensors-clusters', mouseLeaveChangeCursor)\r\n }\r\n }, [map])\r\n\r\n const renderPopup = () => {\r\n if (!popupInfo) return null\r\n const sensorType = sensorTypes.find(t => t.id === popupInfo.typeId)\r\n const liveSensor = sensors.find(s => s.id === popupInfo.id)\r\n const dataUrl = popupInfo.url \r\n ? `${process.env.NEXT_PUBLIC_MINIO_BUCKET_URL}/sensors/${popupInfo.url}`\r\n : popupInfo.data || ''\r\n \r\n return (\r\n <Popup\r\n className=\"noBorderPopup\"\r\n longitude={popupInfo.longitude}\r\n latitude={popupInfo.latitude}\r\n closeOnClick={false}\r\n onClose={() => setPopUpInfo(null)}\r\n anchor=\"bottom\"\r\n style={{ height: '50px', border: 'none', boxShadow: 'none' }}\r\n offset={[0, 10]}\r\n >\r\n <Sensor\r\n sensorName={popupInfo.name || ''}\r\n sensorType={sensorType}\r\n sensorId={popupInfo.id}\r\n tags={liveSensor?.tags ?? []}\r\n onAddTag={async (tag) => { await updateSensor({ tags: [...(liveSensor?.tags ?? []), tag] }) }}\r\n onDeleteTag={async (tag) => { await updateSensor({ tags: (liveSensor?.tags ?? []).filter(t => t !== tag) }) }}\r\n tagsTranslations={{\r\n addTag: tSensors('addTag'),\r\n removeTag: tSensors('removeTag'),\r\n cancel: tSensors('cancel'),\r\n newTagPlaceholder: tSensors('newTagPlaceholder'),\r\n }}\r\n dataUrl={dataUrl}\r\n dataFormat={popupInfo.dataFormat}\r\n updateFrequency={popupInfo.updateFrequency}\r\n buildingId={popupInfo.buildingId}\r\n createdAt={popupInfo.createdAt}\r\n onRemove={user.id === String(popupInfo.authorId) ? handleRemoveSensor : null}\r\n onClose={() => setPopUpInfo(null)}\r\n size=\"sm\"\r\n />\r\n {/* inline styles to override MapLibre's Pop up CSS */}\r\n <style>\r\n {`\r\n .noBorderPopup .maplibregl-popup-content {\r\n border: none !important;\r\n box-shadow: none !important;\r\n background: transparent !important;\r\n }\r\n .noBorderPopup .maplibregl-popup-tip {\r\n display: none !important;\r\n }\r\n `}\r\n </style>\r\n </Popup>\r\n )\r\n }\r\n\r\n // Track unclustered sensors to display icons\r\n const [unclusteredFeatures, setUnclusteredFeatures] = React.useState<MapGeoJSONFeature[]>([])\r\n\r\n React.useEffect(() => {\r\n if (!map) return\r\n\r\n const updateUnclusteredFeatures = () => {\r\n const allFeatures = map.querySourceFeatures('sensors')\r\n const unclusteredOnly = allFeatures.filter(\r\n (f) => !f.properties?.cluster && !f.properties?.point_count\r\n )\r\n // Deduplicate by id\r\n const uniqueFeatures = new Map()\r\n unclusteredOnly.forEach((f) => uniqueFeatures.set(f.properties.id, f))\r\n setUnclusteredFeatures(Array.from(uniqueFeatures.values()))\r\n }\r\n\r\n const onSourceData = (e: any) => {\r\n if (e?.sourceId === 'sensors') updateUnclusteredFeatures()\r\n }\r\n\r\n map.on('sourcedata', onSourceData)\r\n map.on('moveend', updateUnclusteredFeatures)\r\n map.on('zoomend', updateUnclusteredFeatures)\r\n\r\n updateUnclusteredFeatures()\r\n\r\n return () => {\r\n map.off('sourcedata', onSourceData)\r\n map.off('moveend', updateUnclusteredFeatures)\r\n map.off('zoomend', updateUnclusteredFeatures)\r\n }\r\n }, [map])\r\n\r\n return (\r\n <>\r\n {(typesVisible.length > 0 || tagsVisible.length > 0) &&\r\n <Source\r\n id=\"sensors\"\r\n type=\"geojson\"\r\n data={geojsonSensorData}\r\n cluster={true}\r\n clusterMaxZoom={14}\r\n clusterRadius={40}\r\n >\r\n <Layer {...clusterLayer} />\r\n <Layer {...clusterCountLayer} />\r\n <Layer {...unclusteredPointLayer} />\r\n\r\n {renderPopup()}\r\n {unclusteredFeatures\r\n .filter((feature) => feature.properties?.id !== popupInfo?.id)\r\n .map((feature) => (\r\n <SensorIconMarker\r\n key={String(feature.properties?.id)}\r\n feature={feature}\r\n isHighlighted={currentSensorId === feature.properties?.id || hoveredSensorId === feature.properties?.id}\r\n onMouseEnter={() => setHoveredSensorId(feature.properties?.id)}\r\n onMouseLeave={() => setHoveredSensorId(null)}\r\n sensorTypes={sensorTypes}\r\n />\r\n ))}\r\n </Source>}\r\n </>\r\n )\r\n}\r\n"],"mappings":"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","names":["_a","_b","user","t"]}
|
|
1
|
+
{"version":3,"sources":["../../../../../../../../../src/core/components/viewers/map/src/MapLayers/src/SensorsLayer/index.tsx"],"sourcesContent":["'use client'\r\n\r\n// SPDX-License-Identifier: AGPL-3.0-or-later\r\n// Copyright (C) 2025 Collab Digital Twins\r\n\r\nimport { MapContext, MenusContext } from '../../../../../../../store'\r\nimport * as React from 'react'\r\nimport { Source, Layer, Popup, Marker } from 'react-map-gl/maplibre'\r\nimport type { MapGeoJSONFeature, MapLayerMouseEvent } from 'maplibre-gl'\r\nimport Sensor from '../../../../../../ui/Sensors/Sensor'\r\nimport { SensorType, ViewerNames, type Sensor as ISensor} from '../../../../../../../types/dbTypes'\r\n\r\nimport { MapLayerClickPriority, ClickCallback } from '../../../../utils/MapEventManager/MapClickManager'\r\nimport { extractCoordinatesFromFeature } from '../../../../utils/extractCoordinates'\r\nimport { useSensor, useSensors } from '../../../../../../../hooks/sensors/sensors'\r\nimport { useUsers } from '../../../../../../../hooks/users/users'\r\nimport { toast } from 'sonner'\r\nimport { useTranslations } from 'next-intl'\r\nimport { useSession } from 'next-auth/react'\r\nimport { createClusterLayer, createClusterCountLayer, createUnclusteredPointLayer } from '../mapLayersUtils'\r\nimport { useSensorTypes } from '../../../../../../../hooks/sensorTypes/sensorTypes'\r\nimport * as LR from 'lucide-react'\r\nimport { UNTAGGED_TAG } from '../../../../../../ui/Sensors/SensorsSection'\r\n\r\nconst SensorIconMarker = ({ feature, isHighlighted, onMouseEnter, onMouseLeave, sensorTypes }: { feature: MapGeoJSONFeature; isHighlighted?: boolean; onMouseEnter?: () => void; onMouseLeave?: () => void; sensorTypes: SensorType[] }) => {\r\n\r\n const coords = extractCoordinatesFromFeature(feature)\r\n if (!coords) return null\r\n\r\n const sensorType = sensorTypes.find(t => t.id === feature.properties?.typeId)\r\n const icon = sensorType?.icon || 'Radio'\r\n const SensorIcon = LR[icon] || LR.Radio\r\n\r\n return (\r\n <Marker key={String(feature.properties?.id ?? `${coords.lng},${coords.lat}`)} longitude={coords.lng} latitude={coords.lat} anchor=\"center\">\r\n <div\r\n style={{\r\n width: '36px',\r\n height: '36px',\r\n borderRadius: '50%',\r\n border: isHighlighted ? '3px solid #73cee2' : '2px solid white',\r\n backgroundColor: 'black',\r\n display: 'flex',\r\n alignItems: 'center',\r\n justifyContent: 'center',\r\n boxShadow: isHighlighted ? '0 0 12px rgba(115, 206, 226, 0.5)' : 'none',\r\n transition: 'all 0.2s ease-in-out',\r\n transform: isHighlighted ? 'scale(1.2)' : 'scale(1)',\r\n }}\r\n onMouseEnter={onMouseEnter}\r\n onMouseLeave={onMouseLeave}\r\n >\r\n <SensorIcon style={{ width: '20px', height: '20px', color: 'hsl(var(--primary-foreground))' }} />\r\n </div>\r\n </Marker>\r\n )\r\n}\r\n\r\nexport const SensorLayers = ({ minioBaseUrl }: { minioBaseUrl?: string }) => {\r\n const [hoveredSensorId, setHoveredSensorId] = React.useState<number | null>(null)\r\n const clusterLayer = createClusterLayer('sensors')\r\n const clusterCountLayer = createClusterCountLayer('sensors')\r\n const unclusteredPointLayer = createUnclusteredPointLayer('sensors')\r\n\r\n // global map state\r\n\r\n const t = useTranslations('SensorLayers')\r\n const tSensors = useTranslations('SensorsSection')\r\n\r\n const { state: mapState } = React.useContext(MapContext)\r\n const { map, mapClickManager } = mapState.map\r\n const { sensors } = useSensors()\r\n const { users } = useUsers()\r\n const user = useSession().data?.user\r\n\r\n const [popupInfo, setPopUpInfo] = React.useState<Partial<ISensor & { sensorType: SensorType }> | null>(null)\r\n const { deleteSensor, updateSensor } = useSensor(popupInfo?.id ?? null)\r\n const { state: menusState } = React.useContext(MenusContext)\r\n const { visibleSensorTypes, visibleSensorTags, currentSensorId } = menusState.menus\r\n const typesVisible = visibleSensorTypes?.[ViewerNames.map] || []\r\n const tagsVisible = visibleSensorTags?.[ViewerNames.map] || []\r\n\r\n const {sensorTypes} = useSensorTypes()\r\n\r\n const eligibleSensors: Array<ISensor & { authorName: string } & { sensorType: SensorType }> = sensors\r\n .filter((sensor) => sensor.viewer === ViewerNames.map)\r\n .filter((sensor) => {\r\n const matchesType = typesVisible.includes(sensor.typeId)\r\n const hasNoTags = !sensor.tags?.length\r\n const matchesTag = hasNoTags\r\n ? tagsVisible.includes(UNTAGGED_TAG)\r\n : sensor.tags?.some(tag => tagsVisible.includes(tag)) ?? false\r\n return matchesType || matchesTag\r\n })\r\n .map((sensor) => {\r\n const user = users.find(u => u.id === sensor.authorId)\r\n const sensorType = sensorTypes.find(t => t.id === sensor.typeId)\r\n return {\r\n ...sensor,\r\n authorName: user?.name ?? 'Unknown User',\r\n sensorType,\r\n }\r\n })\r\n\r\n // Close popup if the sensor was deleted\r\n React.useEffect(() => {\r\n if (popupInfo && !sensors.find((s) => s.id === popupInfo.id)) {\r\n setPopUpInfo(null)\r\n }\r\n }, [sensors, popupInfo])\r\n\r\n const geojsonSensorData = React.useMemo(() => {\r\n const convertDataToGeojson = (sensorData: typeof eligibleSensors): GeoJSON.FeatureCollection<GeoJSON.Point, { [key: string]: any }> => {\r\n const sensorFeatures: GeoJSON.Feature<GeoJSON.Point, { [key: string]: any }>[] = sensorData\r\n .map((sensor) => {\r\n const { longitude, latitude, id, name, typeId, sensorType, data, dataFormat, updateFrequency, createdAt, authorId, organizationId, visible, url, authorName } = sensor\r\n\r\n return {\r\n type: 'Feature',\r\n geometry: {\r\n type: 'Point',\r\n coordinates: [longitude, latitude],\r\n },\r\n properties: {\r\n id: Number(id),\r\n organizationId,\r\n visible,\r\n longitude,\r\n latitude,\r\n authorId: Number(authorId),\r\n authorName,\r\n name,\r\n typeId: Number(typeId),\r\n typeIcon: sensorType?.icon,\r\n data,\r\n dataFormat,\r\n updateFrequency,\r\n url,\r\n createdAt,\r\n viewer: ViewerNames.map,\r\n },\r\n }\r\n })\r\n\r\n \r\n\r\n const sensorFC: GeoJSON.FeatureCollection<GeoJSON.Point, { [key: string]: any }> = {\r\n type: 'FeatureCollection',\r\n features: sensorFeatures,\r\n }\r\n return sensorFC\r\n }\r\n return convertDataToGeojson(eligibleSensors)\r\n }, [eligibleSensors])\r\n\r\n // event listeners for sensor unclustered points\r\n React.useEffect(() => {\r\n if (!map) return\r\n const showSensorPopUp: ClickCallback = (e: MapLayerMouseEvent, features: MapGeoJSONFeature[]) => {\r\n const feature = features?.[0]\r\n\r\n if (!feature || feature.properties.point_count) return\r\n\r\n if (feature.geometry.type != 'Point') return\r\n const [longitude, latitude] = feature.geometry.coordinates\r\n const { id, authorId, name, typeId, data, dataFormat, updateFrequency, createdAt, url } = feature.properties\r\n setPopUpInfo({\r\n id: Number(id),\r\n authorId: Number(authorId),\r\n organizationId: feature.properties?.organizationId,\r\n visible: feature.properties?.visible,\r\n longitude,\r\n latitude,\r\n name,\r\n typeId: Number(typeId),\r\n data,\r\n dataFormat,\r\n updateFrequency,\r\n url,\r\n createdAt,\r\n viewer: ViewerNames.map,\r\n })\r\n }\r\n\r\n const mouseEnterChangeCursor = () => {\r\n map.getCanvas().style.cursor = 'pointer'\r\n }\r\n const mouseLeaveChangeCursor = () => {\r\n map.getCanvas().style.cursor = ''\r\n }\r\n\r\n // event listener for clicking on single point to show sensor, hover to change cursor\r\n mapClickManager.register('sensors-unclustered-points', MapLayerClickPriority.CommentLayersClickPriority, showSensorPopUp)\r\n\r\n map.on('mouseenter', 'sensors-unclustered-points', mouseEnterChangeCursor)\r\n map.on('mouseleave', 'sensors-unclustered-points', mouseLeaveChangeCursor)\r\n return () => {\r\n mapClickManager.unregister('sensors-unclustered-points')\r\n map.off('mouseenter', 'sensors-unclustered-points', mouseEnterChangeCursor)\r\n map.off('mouseleave', 'sensors-unclustered-points', mouseLeaveChangeCursor)\r\n }\r\n }, [map, mapClickManager])\r\n\r\n const handleRemoveSensor = () => {\r\n toast.success(t('sensorDeleted'))\r\n deleteSensor()\r\n setPopUpInfo(null)\r\n }\r\n\r\n // event listeners for clustered points\r\n React.useEffect(() => {\r\n if (!map) return\r\n\r\n const zoomInToDecluster = (e: MapLayerMouseEvent) => {\r\n const feature = e.features?.[0] // only proceed if this is actually a cluster\r\n if (!feature || !feature.properties.cluster_id) return\r\n\r\n const clusterId = feature.properties.cluster_id as number\r\n\r\n // Check if geometry has coordinates (not a GeometryCollection)\r\n if (!('coordinates' in feature.geometry)) return\r\n const [lng, lat] = feature.geometry.coordinates as [number, number]\r\n\r\n const source = map.getSource('sensors') as any\r\n console.log(source)\r\n console.log(clusterId)\r\n source.getClusterExpansionZoom(clusterId).then(\r\n (zoom: number) => {\r\n console.log('easing to')\r\n map.easeTo({ center: [lng, lat], zoom })\r\n },\r\n )\r\n .catch((error) => {\r\n console.error(error)\r\n })\r\n }\r\n\r\n const mouseEnterChangeCursor = () => { map.getCanvas().style.cursor = 'pointer' }\r\n const mouseLeaveChangeCursor = () => { map.getCanvas().style.cursor = '' }\r\n\r\n // event listener for clicking on single point to zoom in and decluster, hover to change cursor\r\n map.on('click', 'sensors-clusters', zoomInToDecluster)\r\n map.on('mouseenter', 'sensors-clusters', mouseEnterChangeCursor)\r\n map.on('mouseleave', 'sensors-clusters', mouseLeaveChangeCursor)\r\n return () => {\r\n map.off('click', 'sensors-clusters', zoomInToDecluster)\r\n map.off('mouseenter', 'sensors-clusters', mouseEnterChangeCursor)\r\n map.off('mouseleave', 'sensors-clusters', mouseLeaveChangeCursor)\r\n }\r\n }, [map])\r\n\r\n const renderPopup = () => {\r\n if (!popupInfo) return null\r\n const sensorType = sensorTypes.find(t => t.id === popupInfo.typeId)\r\n const liveSensor = sensors.find(s => s.id === popupInfo.id)\r\n const dataUrl = popupInfo.url\r\n ? `${minioBaseUrl ?? ''}/sensors/${popupInfo.url}`\r\n : popupInfo.data || ''\r\n \r\n return (\r\n <Popup\r\n className=\"noBorderPopup\"\r\n longitude={popupInfo.longitude}\r\n latitude={popupInfo.latitude}\r\n closeOnClick={false}\r\n onClose={() => setPopUpInfo(null)}\r\n anchor=\"bottom\"\r\n style={{ height: '50px', border: 'none', boxShadow: 'none' }}\r\n offset={[0, 10]}\r\n >\r\n <Sensor\r\n sensorName={popupInfo.name || ''}\r\n sensorType={sensorType}\r\n sensorId={popupInfo.id}\r\n tags={liveSensor?.tags ?? []}\r\n onAddTag={async (tag) => { await updateSensor({ tags: [...(liveSensor?.tags ?? []), tag] }) }}\r\n onDeleteTag={async (tag) => { await updateSensor({ tags: (liveSensor?.tags ?? []).filter(t => t !== tag) }) }}\r\n tagsTranslations={{\r\n addTag: tSensors('addTag'),\r\n removeTag: tSensors('removeTag'),\r\n cancel: tSensors('cancel'),\r\n newTagPlaceholder: tSensors('newTagPlaceholder'),\r\n }}\r\n dataUrl={dataUrl}\r\n dataFormat={popupInfo.dataFormat}\r\n updateFrequency={popupInfo.updateFrequency}\r\n buildingId={popupInfo.buildingId}\r\n createdAt={popupInfo.createdAt}\r\n onRemove={user.id === String(popupInfo.authorId) ? handleRemoveSensor : null}\r\n onClose={() => setPopUpInfo(null)}\r\n size=\"sm\"\r\n />\r\n {/* inline styles to override MapLibre's Pop up CSS */}\r\n <style>\r\n {`\r\n .noBorderPopup .maplibregl-popup-content {\r\n border: none !important;\r\n box-shadow: none !important;\r\n background: transparent !important;\r\n }\r\n .noBorderPopup .maplibregl-popup-tip {\r\n display: none !important;\r\n }\r\n `}\r\n </style>\r\n </Popup>\r\n )\r\n }\r\n\r\n // Track unclustered sensors to display icons\r\n const [unclusteredFeatures, setUnclusteredFeatures] = React.useState<MapGeoJSONFeature[]>([])\r\n\r\n React.useEffect(() => {\r\n if (!map) return\r\n\r\n const updateUnclusteredFeatures = () => {\r\n const allFeatures = map.querySourceFeatures('sensors')\r\n const unclusteredOnly = allFeatures.filter(\r\n (f) => !f.properties?.cluster && !f.properties?.point_count\r\n )\r\n // Deduplicate by id\r\n const uniqueFeatures = new Map()\r\n unclusteredOnly.forEach((f) => uniqueFeatures.set(f.properties.id, f))\r\n setUnclusteredFeatures(Array.from(uniqueFeatures.values()))\r\n }\r\n\r\n const onSourceData = (e: any) => {\r\n if (e?.sourceId === 'sensors') updateUnclusteredFeatures()\r\n }\r\n\r\n map.on('sourcedata', onSourceData)\r\n map.on('moveend', updateUnclusteredFeatures)\r\n map.on('zoomend', updateUnclusteredFeatures)\r\n\r\n updateUnclusteredFeatures()\r\n\r\n return () => {\r\n map.off('sourcedata', onSourceData)\r\n map.off('moveend', updateUnclusteredFeatures)\r\n map.off('zoomend', updateUnclusteredFeatures)\r\n }\r\n }, [map])\r\n\r\n return (\r\n <>\r\n {(typesVisible.length > 0 || tagsVisible.length > 0) &&\r\n <Source\r\n id=\"sensors\"\r\n type=\"geojson\"\r\n data={geojsonSensorData}\r\n cluster={true}\r\n clusterMaxZoom={14}\r\n clusterRadius={40}\r\n >\r\n <Layer {...clusterLayer} />\r\n <Layer {...clusterCountLayer} />\r\n <Layer {...unclusteredPointLayer} />\r\n\r\n {renderPopup()}\r\n {unclusteredFeatures\r\n .filter((feature) => feature.properties?.id !== popupInfo?.id)\r\n .map((feature) => (\r\n <SensorIconMarker\r\n key={String(feature.properties?.id)}\r\n feature={feature}\r\n isHighlighted={currentSensorId === feature.properties?.id || hoveredSensorId === feature.properties?.id}\r\n onMouseEnter={() => setHoveredSensorId(feature.properties?.id)}\r\n onMouseLeave={() => setHoveredSensorId(null)}\r\n sensorTypes={sensorTypes}\r\n />\r\n ))}\r\n </Source>}\r\n </>\r\n )\r\n}\r\n"],"mappings":"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","names":["_a","_b","user","t"]}
|
|
@@ -1,3 +1,8 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
|
-
|
|
2
|
+
import { Organization } from '../../../../../types/dbTypes';
|
|
3
|
+
export declare function MapSidebar({ minioBaseUrl, martinBaseUrl, organization }: {
|
|
4
|
+
minioBaseUrl?: string;
|
|
5
|
+
martinBaseUrl?: string;
|
|
6
|
+
organization?: Organization;
|
|
7
|
+
}): React.JSX.Element;
|
|
3
8
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../../../src/core/components/viewers/map/src/MapSidebar/index.tsx"],"names":[],"mappings":"AAKA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../../../src/core/components/viewers/map/src/MapSidebar/index.tsx"],"names":[],"mappings":"AAKA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAW9B,OAAO,EAAE,YAAY,EAAE,MAAM,8BAA8B,CAAA;AAE3D,wBAAgB,UAAU,CAAC,EAAE,YAAY,EAAE,aAAa,EAAE,YAAY,EAAE,EAAE;IAAE,YAAY,CAAC,EAAE,MAAM,CAAC;IAAC,aAAa,CAAC,EAAE,MAAM,CAAC;IAAC,YAAY,CAAC,EAAE,YAAY,CAAA;CAAE,qBA4BvJ"}
|
|
@@ -9,7 +9,7 @@ import { SettingsTab } from "./src/SettingsTab";
|
|
|
9
9
|
import { InfoSidebarContainer } from "../../../../../components/ui/InfoSidebar/Container";
|
|
10
10
|
import { CommunicationTab } from "./src/CommunicationTab";
|
|
11
11
|
import { SensorsTab } from "./src/SensorsTab";
|
|
12
|
-
function MapSidebar() {
|
|
12
|
+
function MapSidebar({ minioBaseUrl, martinBaseUrl, organization }) {
|
|
13
13
|
const { ability } = usePermissions();
|
|
14
14
|
const { state: menusState, dispatch: menusDispatch } = React.useContext(MenusContext);
|
|
15
15
|
const { selectedTab } = menusState.menus;
|
|
@@ -19,6 +19,7 @@ function MapSidebar() {
|
|
|
19
19
|
return /* @__PURE__ */ jsxs(
|
|
20
20
|
InfoSidebarContainer,
|
|
21
21
|
{
|
|
22
|
+
organization,
|
|
22
23
|
tabSelector: /* @__PURE__ */ jsx(
|
|
23
24
|
TabSelector,
|
|
24
25
|
{
|
|
@@ -28,10 +29,10 @@ function MapSidebar() {
|
|
|
28
29
|
),
|
|
29
30
|
children: [
|
|
30
31
|
selectedTab === "file" && ability.can("read", "File") && /* @__PURE__ */ jsx(FileTab, {}),
|
|
31
|
-
selectedTab === "layers" && ability.can("read", "File") && /* @__PURE__ */ jsx(LayersTab, {}),
|
|
32
|
+
selectedTab === "layers" && ability.can("read", "File") && /* @__PURE__ */ jsx(LayersTab, { martinBaseUrl, organization }),
|
|
32
33
|
selectedTab === "communication" && ability.can("read", "Comment") && /* @__PURE__ */ jsx(CommunicationTab, {}),
|
|
33
|
-
selectedTab === "sensors" && ability.can("read", "Sensor") && /* @__PURE__ */ jsx(SensorsTab, {}),
|
|
34
|
-
selectedTab === "settings" && /* @__PURE__ */ jsx(SettingsTab, {})
|
|
34
|
+
selectedTab === "sensors" && ability.can("read", "Sensor") && /* @__PURE__ */ jsx(SensorsTab, { minioBaseUrl }),
|
|
35
|
+
selectedTab === "settings" && /* @__PURE__ */ jsx(SettingsTab, { countryCode: organization == null ? void 0 : organization.country })
|
|
35
36
|
]
|
|
36
37
|
}
|
|
37
38
|
);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../../src/core/components/viewers/map/src/MapSidebar/index.tsx"],"sourcesContent":["'use client'\r\n\r\n// SPDX-License-Identifier: AGPL-3.0-or-later\r\n// Copyright (C) 2025 Collab Digital Twins\r\n\r\nimport * as React from 'react'\r\nimport { TabSelector } from './TabSelector'\r\nimport { MenusContext, usePermissions } from '../../../../../store'\r\nimport { SidebarTabType } from '../../../../../store/Menus/reducer'\r\nimport { FileTab } from './src/FileTab'\r\nimport { LayersTab } from './src/LayersTab'\r\n\r\nimport { SettingsTab } from './src/SettingsTab'\r\nimport { InfoSidebarContainer } from '../../../../../components/ui/InfoSidebar/Container'\r\nimport { CommunicationTab } from './src/CommunicationTab'\r\nimport { SensorsTab } from './src/SensorsTab'\r\n\r\nexport function MapSidebar() {\r\n // Permissions\r\n const { ability } = usePermissions()\r\n\r\n const { state: menusState, dispatch: menusDispatch } = React.useContext(MenusContext)\r\n const { selectedTab } = menusState.menus\r\n\r\n const handleTabChange = (selectedTab: SidebarTabType) => {\r\n menusDispatch({ type: 'SET_SIDEBAR_SELECTED_TAB', payload: { selectedTab } })\r\n }\r\n\r\n return (\r\n <InfoSidebarContainer\r\n tabSelector={\r\n <TabSelector\r\n activeTab={selectedTab}\r\n onTabChangeAction={handleTabChange}\r\n />\r\n }\r\n >\r\n {selectedTab === 'file' && ability.can('read', 'File') && <FileTab />}\r\n {selectedTab === 'layers' && ability.can('read', 'File') && <LayersTab />}\r\n {selectedTab === 'communication' && ability.can('read', 'Comment') && <CommunicationTab />}\r\n {selectedTab === 'sensors' && ability.can('read', 'Sensor') && <SensorsTab />}\r\n {selectedTab === 'settings' && <SettingsTab />}
|
|
1
|
+
{"version":3,"sources":["../../../../../../../src/core/components/viewers/map/src/MapSidebar/index.tsx"],"sourcesContent":["'use client'\r\n\r\n// SPDX-License-Identifier: AGPL-3.0-or-later\r\n// Copyright (C) 2025 Collab Digital Twins\r\n\r\nimport * as React from 'react'\r\nimport { TabSelector } from './TabSelector'\r\nimport { MenusContext, usePermissions } from '../../../../../store'\r\nimport { SidebarTabType } from '../../../../../store/Menus/reducer'\r\nimport { FileTab } from './src/FileTab'\r\nimport { LayersTab } from './src/LayersTab'\r\n\r\nimport { SettingsTab } from './src/SettingsTab'\r\nimport { InfoSidebarContainer } from '../../../../../components/ui/InfoSidebar/Container'\r\nimport { CommunicationTab } from './src/CommunicationTab'\r\nimport { SensorsTab } from './src/SensorsTab'\r\nimport { Organization } from '../../../../../types/dbTypes'\r\n\r\nexport function MapSidebar({ minioBaseUrl, martinBaseUrl, organization }: { minioBaseUrl?: string; martinBaseUrl?: string; organization?: Organization }) {\r\n // Permissions\r\n const { ability } = usePermissions()\r\n\r\n const { state: menusState, dispatch: menusDispatch } = React.useContext(MenusContext)\r\n const { selectedTab } = menusState.menus\r\n\r\n const handleTabChange = (selectedTab: SidebarTabType) => {\r\n menusDispatch({ type: 'SET_SIDEBAR_SELECTED_TAB', payload: { selectedTab } })\r\n }\r\n\r\n return (\r\n <InfoSidebarContainer\r\n organization={organization}\r\n tabSelector={\r\n <TabSelector\r\n activeTab={selectedTab}\r\n onTabChangeAction={handleTabChange}\r\n />\r\n }\r\n >\r\n {selectedTab === 'file' && ability.can('read', 'File') && <FileTab />}\r\n {selectedTab === 'layers' && ability.can('read', 'File') && <LayersTab martinBaseUrl={martinBaseUrl} organization={organization} />}\r\n {selectedTab === 'communication' && ability.can('read', 'Comment') && <CommunicationTab />}\r\n {selectedTab === 'sensors' && ability.can('read', 'Sensor') && <SensorsTab minioBaseUrl={minioBaseUrl} />}\r\n {selectedTab === 'settings' && <SettingsTab countryCode={organization?.country} />}\r\n </InfoSidebarContainer>\r\n )\r\n}"],"mappings":";AA8BI,SAGI,KAHJ;AAzBJ,YAAY,WAAW;AACvB,SAAS,mBAAmB;AAC5B,SAAS,cAAc,sBAAsB;AAE7C,SAAS,eAAe;AACxB,SAAS,iBAAiB;AAE1B,SAAS,mBAAmB;AAC5B,SAAS,4BAA4B;AACrC,SAAS,wBAAwB;AACjC,SAAS,kBAAkB;AAGpB,SAAS,WAAW,EAAE,cAAc,eAAe,aAAa,GAAmF;AAExJ,QAAM,EAAE,QAAQ,IAAI,eAAe;AAEnC,QAAM,EAAE,OAAO,YAAY,UAAU,cAAc,IAAI,MAAM,WAAW,YAAY;AACpF,QAAM,EAAE,YAAY,IAAI,WAAW;AAEnC,QAAM,kBAAkB,CAACA,iBAAgC;AACvD,kBAAc,EAAE,MAAM,4BAA4B,SAAS,EAAE,aAAAA,aAAY,EAAE,CAAC;AAAA,EAC9E;AAEA,SACE;AAAA,IAAC;AAAA;AAAA,MACC;AAAA,MACA,aACE;AAAA,QAAC;AAAA;AAAA,UACC,WAAW;AAAA,UACX,mBAAmB;AAAA;AAAA,MACrB;AAAA,MAGD;AAAA,wBAAgB,UAAU,QAAQ,IAAI,QAAQ,MAAM,KAAK,oBAAC,WAAQ;AAAA,QAClE,gBAAgB,YAAY,QAAQ,IAAI,QAAQ,MAAM,KAAK,oBAAC,aAAU,eAA8B,cAA4B;AAAA,QAChI,gBAAgB,mBAAmB,QAAQ,IAAI,QAAQ,SAAS,KAAK,oBAAC,oBAAiB;AAAA,QACvF,gBAAgB,aAAa,QAAQ,IAAI,QAAQ,QAAQ,KAAK,oBAAC,cAAW,cAA4B;AAAA,QACtG,gBAAgB,cAAc,oBAAC,eAAY,aAAa,6CAAc,SAAS;AAAA;AAAA;AAAA,EAClF;AAEJ;","names":["selectedTab"]}
|
|
@@ -1,3 +1,7 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
|
-
|
|
2
|
+
import { Organization } from '../../../../../../../types/dbTypes';
|
|
3
|
+
export declare function LayersTab({ martinBaseUrl, organization }: {
|
|
4
|
+
martinBaseUrl?: string;
|
|
5
|
+
organization?: Organization;
|
|
6
|
+
}): React.JSX.Element;
|
|
3
7
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../../../../../src/core/components/viewers/map/src/MapSidebar/src/LayersTab/index.tsx"],"names":[],"mappings":"AAKA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../../../../../src/core/components/viewers/map/src/MapSidebar/src/LayersTab/index.tsx"],"names":[],"mappings":"AAKA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAU9B,OAAO,EAAgB,YAAY,EAAE,MAAM,oCAAoC,CAAA;AA+E/E,wBAAgB,SAAS,CAAC,EAAE,aAAa,EAAE,YAAY,EAAE,EAAE;IAAE,aAAa,CAAC,EAAE,MAAM,CAAC;IAAC,YAAY,CAAC,EAAE,YAAY,CAAA;CAAE,qBAuJjH"}
|