datastake-daf 0.6.265 β†’ 0.6.267

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.
@@ -18246,7 +18246,6 @@ function useMapHelper$1({
18246
18246
 
18247
18247
  // Actions to do when active marker is changed
18248
18248
  React.useEffect(() => {
18249
- console.log("RERENDER");
18250
18249
  if (mapRef) {
18251
18250
  if (activeMarker) {
18252
18251
  mapRef.eachLayer(layer => {
@@ -19929,14 +19928,26 @@ const useGlobe = ({
19929
19928
  const addedSources = React.useRef(new Set());
19930
19929
  const isMounted = React.useRef(true);
19931
19930
  const addAllDataToMap = React.useCallback(() => {
19932
- if (!data || !mapRef || !mapRef.isStyleLoaded()) {
19931
+ console.log('πŸš€ [GLOBE HOOK] addAllDataToMap called');
19932
+ console.log('πŸš€ [GLOBE HOOK] data:', data);
19933
+ console.log('πŸš€ [GLOBE HOOK] mapRef exists:', !!mapRef);
19934
+ console.log('πŸš€ [GLOBE HOOK] style loaded:', mapRef?.isStyleLoaded());
19935
+ if (!data || !mapRef) {
19936
+ console.log('❌ [GLOBE HOOK] addAllDataToMap early return - missing data or mapRef');
19937
+ return;
19938
+ }
19939
+ if (!mapRef.isStyleLoaded()) {
19940
+ console.log('⏳ [GLOBE HOOK] Style not loaded yet, retrying in 100ms...');
19941
+ setTimeout(() => {
19942
+ addAllDataToMap();
19943
+ }, 100);
19933
19944
  return;
19934
19945
  }
19935
-
19936
- // Prevent multiple calls with empty data
19937
19946
  if (data.length === 0) {
19947
+ console.log('❌ [GLOBE HOOK] addAllDataToMap early return - no data');
19938
19948
  return;
19939
19949
  }
19950
+ console.log('βœ… [GLOBE HOOK] Starting to add markers to map...');
19940
19951
 
19941
19952
  // Clear existing markers using functional update to avoid dependency issues
19942
19953
  setMapMarkers(currentMarkers => {
@@ -19988,8 +19999,13 @@ const useGlobe = ({
19988
19999
  if (data && mapRef) {
19989
20000
  const newMarkers = [];
19990
20001
  const maxTotal = Math.max(...(data || []).map(d => d.total || 0));
20002
+ console.log('🎯 [GLOBE HOOK] Processing data items:', data.length);
19991
20003
  data.forEach((d, i) => {
20004
+ console.log(`🎯 [GLOBE HOOK] Processing item ${i}:`, d);
20005
+ console.log(`🎯 [GLOBE HOOK] Item ${i} marker:`, d?.marker);
20006
+ console.log(`🎯 [GLOBE HOOK] Item ${i} lat/lng:`, d?.marker?.lat, d?.marker?.lng);
19992
20007
  if (d?.marker?.lat && d?.marker?.lng) {
20008
+ console.log(`βœ… [GLOBE HOOK] Item ${i} has valid coordinates, creating marker...`);
19993
20009
  let marker;
19994
20010
  let iconClassName = "";
19995
20011
  let iconSize = [25, 25];
@@ -20069,6 +20085,7 @@ const useGlobe = ({
20069
20085
  el.style.fontWeight = 'bold';
20070
20086
  el.innerHTML = `<span>${d.total || 0}</span>`;
20071
20087
  marker = new mapboxgl.Marker(el).setLngLat([d.marker.lng, d.marker.lat]).setPopup(new mapboxgl.Popup().setDOMContent(div)).addTo(mapRef);
20088
+ console.log(`πŸŽ‰ [GLOBE HOOK] Marker ${i} added to map at:`, [d.marker.lng, d.marker.lat]);
20072
20089
  } else if (type === "territory") {
20073
20090
  // Handle territory polygons
20074
20091
  if (d.area && Array.isArray(d.area)) {
@@ -20138,6 +20155,7 @@ const useGlobe = ({
20138
20155
  }));
20139
20156
  roots.current.push(root);
20140
20157
  marker = new mapboxgl.Marker(el).setLngLat([d.marker.lng, d.marker.lat]).setPopup(new mapboxgl.Popup().setDOMContent(div)).addTo(mapRef);
20158
+ console.log(`πŸŽ‰ [GLOBE HOOK] Default marker ${i} added to map at:`, [d.marker.lng, d.marker.lat]);
20141
20159
  }
20142
20160
 
20143
20161
  // Add click handler
@@ -20171,6 +20189,7 @@ const useGlobe = ({
20171
20189
  mapboxgl.accessToken = MAP_TOKEN;
20172
20190
 
20173
20191
  // Create the map with Mapbox GL JS - 3D globe
20192
+ console.log('πŸ—ΊοΈ [GLOBE HOOK] Creating map with style:', STYLE_URL);
20174
20193
  const map = new mapboxgl.Map({
20175
20194
  container: container.current,
20176
20195
  style: STYLE_URL,
@@ -20189,8 +20208,13 @@ const useGlobe = ({
20189
20208
 
20190
20209
  // Configure the map when style loads
20191
20210
  map.on('style.load', () => {
20211
+ console.log('🎨 [GLOBE HOOK] Style loaded event triggered');
20212
+ console.log('🎨 [GLOBE HOOK] Map style loaded:', map.isStyleLoaded());
20213
+
20192
20214
  // Wait a bit for the style to fully load
20193
20215
  setTimeout(() => {
20216
+ console.log('🎨 [GLOBE HOOK] After timeout - Map style loaded:', map.isStyleLoaded());
20217
+
20194
20218
  // Set fog for the space background effect with stars - simplified to avoid errors
20195
20219
  try {
20196
20220
  map.setFog({
@@ -20286,9 +20310,10 @@ const useGlobe = ({
20286
20310
 
20287
20311
  // Add navigation controls
20288
20312
  map.addControl(new mapboxgl.NavigationControl(), 'top-right');
20313
+ console.log('πŸ—ΊοΈ [GLOBE HOOK] Map created successfully');
20289
20314
  return map;
20290
20315
  } catch (error) {
20291
- console.error('Error creating Mapbox GL JS globe:', error);
20316
+ console.error('❌ [GLOBE HOOK] Error creating Mapbox GL JS globe:', error);
20292
20317
  return null;
20293
20318
  }
20294
20319
  }, []);
@@ -20301,9 +20326,12 @@ const useGlobe = ({
20301
20326
  // }, [initialMarkerSetIsDone]);
20302
20327
 
20303
20328
  React.useEffect(() => {
20329
+ console.log('πŸ”„ [GLOBE HOOK] useEffect for map creation - mapRef:', !!mapRef);
20304
20330
  if (!mapRef) {
20331
+ console.log('πŸ”„ [GLOBE HOOK] Creating map instance...');
20305
20332
  const instance = createInstance();
20306
20333
  if (instance) {
20334
+ console.log('πŸ”„ [GLOBE HOOK] Map instance created, setting mapRef');
20307
20335
  setMapRef(instance);
20308
20336
 
20309
20337
  // Add comprehensive resize detection for Mapbox GL JS responsiveness
@@ -20384,14 +20412,22 @@ const useGlobe = ({
20384
20412
  }
20385
20413
  }, [polygon, mapRef]);
20386
20414
  React.useEffect(() => {
20415
+ console.log('πŸ“₯ [GLOBE HOOK] allData received:', allData);
20416
+ console.log('πŸ“₯ [GLOBE HOOK] allData length:', allData?.length);
20387
20417
  if (allData) {
20388
20418
  if (allData.length === 0) {
20419
+ console.log('⚠️ [GLOBE HOOK] Empty data array');
20389
20420
  setEmptyStateIsVisible(true);
20390
20421
  } else if (emptyStateIsVisible) {
20391
20422
  setEmptyStateIsVisible(false);
20392
20423
  }
20393
- setData(filterValidGPS(allData));
20424
+ console.log('πŸ” [GLOBE HOOK] Filtering data with filterValidGPS...');
20425
+ const filteredData = filterValidGPS(allData);
20426
+ console.log('πŸ” [GLOBE HOOK] filtered data result:', filteredData);
20427
+ console.log('πŸ” [GLOBE HOOK] filtered data length:', filteredData.length);
20428
+ setData(filteredData);
20394
20429
  } else {
20430
+ console.log('❌ [GLOBE HOOK] No allData provided');
20395
20431
  setData(null);
20396
20432
  }
20397
20433
  }, [allData, emptyStateIsVisible]);
@@ -20421,7 +20457,19 @@ const useGlobe = ({
20421
20457
  }
20422
20458
  }, [user, mapRef, allData]);
20423
20459
  React.useEffect(() => {
20424
- if (mapRef && data && !initialMarkerSetIsDone && mapRef.isStyleLoaded()) {
20460
+ console.log('πŸ”„ [GLOBE HOOK] useEffect triggered:', {
20461
+ mapRef: !!mapRef,
20462
+ data: !!data,
20463
+ dataLength: data?.length,
20464
+ initialMarkerSetIsDone,
20465
+ styleLoaded: mapRef?.isStyleLoaded()
20466
+ });
20467
+ if (mapRef && data && !initialMarkerSetIsDone) {
20468
+ console.log('πŸš€ [GLOBE HOOK] Attempting to add markers...');
20469
+ console.log('πŸš€ [GLOBE HOOK] Style loaded check:', mapRef.isStyleLoaded());
20470
+
20471
+ // Try to add markers immediately, and if style isn't loaded,
20472
+ // the addAllDataToMap function will handle it
20425
20473
  setInitialMarkerSetIsDone(true);
20426
20474
  addAllDataToMap();
20427
20475
  }
@@ -20634,7 +20682,7 @@ function Globe(_ref) {
20634
20682
  renderSider = null,
20635
20683
  renderMarker = null,
20636
20684
  type = "default",
20637
- showSider = true,
20685
+ showSider = false,
20638
20686
  filtersConfig,
20639
20687
  onFilterChange = () => {},
20640
20688
  isSatellite = false,
@@ -20644,15 +20692,29 @@ function Globe(_ref) {
20644
20692
  onUnmount
20645
20693
  } = _ref;
20646
20694
  // Map data to include marker coordinates
20647
- const mappedData = React.useMemo(() => data.map(d => {
20648
- var _d$gps, _d$gps2;
20649
- return _objectSpread2(_objectSpread2({}, d), {}, {
20650
- marker: {
20651
- lat: d === null || d === void 0 || (_d$gps = d.gps) === null || _d$gps === void 0 ? void 0 : _d$gps.latitude,
20652
- lng: d === null || d === void 0 || (_d$gps2 = d.gps) === null || _d$gps2 === void 0 ? void 0 : _d$gps2.longitude
20653
- }
20695
+ const mappedData = React.useMemo(() => {
20696
+ console.log('πŸ“Š [GLOBE COMPONENT] Original data received:', data);
20697
+ console.log('πŸ“Š [GLOBE COMPONENT] Data length:', data === null || data === void 0 ? void 0 : data.length);
20698
+ if (!data || data.length === 0) {
20699
+ console.log('❌ [GLOBE COMPONENT] No data to map');
20700
+ return [];
20701
+ }
20702
+ const mapped = data.map((d, i) => {
20703
+ var _d$gps, _d$gps2;
20704
+ console.log("\uD83D\uDCCA [GLOBE COMPONENT] Mapping item ".concat(i, ":"), d);
20705
+ console.log("\uD83D\uDCCA [GLOBE COMPONENT] Item ".concat(i, " GPS:"), d === null || d === void 0 ? void 0 : d.gps);
20706
+ const result = _objectSpread2(_objectSpread2({}, d), {}, {
20707
+ marker: {
20708
+ lat: d === null || d === void 0 || (_d$gps = d.gps) === null || _d$gps === void 0 ? void 0 : _d$gps.latitude,
20709
+ lng: d === null || d === void 0 || (_d$gps2 = d.gps) === null || _d$gps2 === void 0 ? void 0 : _d$gps2.longitude
20710
+ }
20711
+ });
20712
+ console.log("\uD83D\uDCCA [GLOBE COMPONENT] Item ".concat(i, " mapped result:"), result);
20713
+ return result;
20654
20714
  });
20655
- }), [data]);
20715
+ console.log('πŸ“Š [GLOBE COMPONENT] Final mapped data:', mapped);
20716
+ return mapped;
20717
+ }, [data]);
20656
20718
 
20657
20719
  // Get resize context for sidebar state changes
20658
20720
  const {
@@ -20836,6 +20898,162 @@ Globe.propTypes = {
20836
20898
  nameAsSiderTitle: PropTypes__default["default"].bool
20837
20899
  };
20838
20900
 
20901
+ const SimpleGlobe = _ref => {
20902
+ let {
20903
+ projects = [],
20904
+ mapConfig = {},
20905
+ showSider = false,
20906
+ onProjectClick = () => {}
20907
+ } = _ref;
20908
+ const mapContainer = React.useRef(null);
20909
+ const map = React.useRef(null);
20910
+ React.useEffect(() => {
20911
+ if (map.current) return; // Initialize map only once
20912
+
20913
+ console.log('πŸ—ΊοΈ [SIMPLE GLOBE] Creating map...');
20914
+
20915
+ // Set Mapbox access token
20916
+ mapboxgl.accessToken = 'pk.eyJ1IjoicmVkaXM5OTkiLCJhIjoiY2x4YWV5MzA5MmtuZzJpcXM5Y201Z2E2YiJ9.m5bwPg-Tj4Akesl1yQUa3w';
20917
+
20918
+ // Create map with custom Straatos style
20919
+ map.current = new mapboxgl.Map({
20920
+ container: mapContainer.current,
20921
+ style: 'mapbox://styles/pietragottardo/cm9tt9zfi00d101pg1vdx26si',
20922
+ center: [0, 0],
20923
+ zoom: mapConfig.maxZoom || 3,
20924
+ projection: 'globe',
20925
+ attributionControl: false
20926
+ });
20927
+
20928
+ // Add markers when map loads
20929
+ map.current.on('load', () => {
20930
+ console.log('πŸ—ΊοΈ [SIMPLE GLOBE] Map loaded, adding markers...');
20931
+
20932
+ // Hide Mapbox logo and attribution completely
20933
+ map.current.getContainer();
20934
+ const style = document.createElement('style');
20935
+ style.textContent = "\n .mapboxgl-ctrl-logo,\n .mapboxgl-ctrl-attrib,\n .mapboxgl-ctrl-bottom-left,\n .mapboxgl-ctrl-bottom-right {\n display: none !important;\n }\n ";
20936
+ document.head.appendChild(style);
20937
+
20938
+ // Set the space background with stars
20939
+ try {
20940
+ map.current.setFog({
20941
+ 'color': 'rgb(0, 0, 0)',
20942
+ 'high-color': 'rgb(0, 0, 0)',
20943
+ 'horizon-blend': 0.1,
20944
+ 'space-color': 'rgb(0, 0, 0)',
20945
+ 'star-intensity': 0.6
20946
+ });
20947
+ console.log('✨ [SIMPLE GLOBE] Space background with stars set');
20948
+ } catch (e) {
20949
+ console.log('⚠️ [SIMPLE GLOBE] Could not set fog, trying alternative...');
20950
+ // Fallback: try simpler fog configuration
20951
+ try {
20952
+ map.current.setFog({
20953
+ 'color': 'rgb(0, 0, 0)',
20954
+ 'high-color': 'rgb(0, 0, 0)',
20955
+ 'horizon-blend': 0.1
20956
+ });
20957
+ console.log('✨ [SIMPLE GLOBE] Alternative space background set');
20958
+ } catch (e2) {
20959
+ console.log('⚠️ [SIMPLE GLOBE] Could not set any fog configuration');
20960
+ }
20961
+ }
20962
+
20963
+ // Calculate bounds to fit all markers
20964
+ const bounds = new mapboxgl.LngLatBounds();
20965
+ let hasValidCoordinates = false;
20966
+ projects.forEach((project, index) => {
20967
+ var _project$author;
20968
+ console.log("\uD83D\uDCCD [SIMPLE GLOBE] Adding marker ".concat(index, ":"), project);
20969
+
20970
+ // Create marker element
20971
+ const el = document.createElement('div');
20972
+ el.className = 'mapboxgl-marker';
20973
+ el.style.width = '25px';
20974
+ el.style.height = '25px';
20975
+ el.style.backgroundColor = '#00809E';
20976
+ el.style.borderRadius = '50%';
20977
+ el.style.border = '2px solid white';
20978
+ el.style.cursor = 'pointer';
20979
+ el.style.boxShadow = '0px 3.45px 3.45px 0px #00000029';
20980
+ el.style.display = 'flex';
20981
+ el.style.alignItems = 'center';
20982
+ el.style.justifyContent = 'center';
20983
+ el.style.color = 'white';
20984
+ el.style.fontWeight = 'bold';
20985
+ el.style.fontSize = '12px';
20986
+ el.innerHTML = "<span>".concat(project.percentageCompletion || 0, "</span>");
20987
+
20988
+ // Create popup content using the same structure as minesitemap
20989
+ const popupContent = "\n <div class=\"daf-tooltip-cont\">\n <div class=\"daf-tooltip-head\">\n <div class=\"daf-tooltip-title\">\n <div>\n <h4>".concat(project.name, "</h4>\n <h5>").concat(project.sectoralScope || 'Project', "</h5>\n </div>\n </div>\n </div>\n <div class=\"daf-tooltip-list\">\n <div class=\"daf-tooltip-list-item\">\n <span class=\"daf-tooltip-name\">Country</span>\n <span class=\"daf-tooltip-value\">").concat(project.country || 'N/A', "</span>\n </div>\n <div class=\"daf-tooltip-list-item\">\n <span class=\"daf-tooltip-name\">Completion</span>\n <span class=\"daf-tooltip-value\">").concat(project.percentageCompletion || 0, "%</span>\n </div>\n <div class=\"daf-tooltip-list-item\">\n <span class=\"daf-tooltip-name\">Author</span>\n <span class=\"daf-tooltip-value\">").concat(((_project$author = project.author) === null || _project$author === void 0 ? void 0 : _project$author.name) || 'N/A', "</span>\n </div>\n <div class=\"daf-tooltip-list-item\">\n <span class=\"daf-tooltip-name\">ID</span>\n <span class=\"daf-tooltip-value\">").concat(project.datastakeId || 'N/A', "</span>\n </div>\n </div>\n </div>\n ");
20990
+
20991
+ // Create popup
20992
+ const popup = new mapboxgl.Popup({
20993
+ offset: 25,
20994
+ closeButton: true,
20995
+ closeOnClick: false
20996
+ }).setHTML(popupContent);
20997
+
20998
+ // Ensure coordinates are valid numbers
20999
+ const lng = Number(project.longitude);
21000
+ const lat = Number(project.latitude);
21001
+ console.log("\uD83D\uDCCD [SIMPLE GLOBE] Marker ".concat(index, " coordinates:"), {
21002
+ lng,
21003
+ lat
21004
+ });
21005
+
21006
+ // Validate coordinates
21007
+ if (isNaN(lng) || isNaN(lat) || lng < -180 || lng > 180 || lat < -90 || lat > 90) {
21008
+ console.error("\u274C [SIMPLE GLOBE] Invalid coordinates for project ".concat(index, ":"), {
21009
+ lng,
21010
+ lat
21011
+ });
21012
+ return;
21013
+ }
21014
+
21015
+ // Add coordinates to bounds
21016
+ bounds.extend([lng, lat]);
21017
+ hasValidCoordinates = true;
21018
+
21019
+ // Add marker to map with proper coordinate order [lng, lat]
21020
+ new mapboxgl.Marker(el).setLngLat([lng, lat]).setPopup(popup).addTo(map.current);
21021
+
21022
+ // Add click handler
21023
+ el.addEventListener('click', () => {
21024
+ console.log('πŸ“ [SIMPLE GLOBE] Marker clicked:', project);
21025
+ onProjectClick(project);
21026
+ });
21027
+ console.log("\u2705 [SIMPLE GLOBE] Marker ".concat(index, " added at:"), [lng, lat]);
21028
+ });
21029
+
21030
+ // Fit map to show all markers if we have valid coordinates
21031
+ if (hasValidCoordinates && !bounds.isEmpty()) {
21032
+ console.log('πŸ—ΊοΈ [SIMPLE GLOBE] Fitting map to bounds:', bounds);
21033
+ map.current.fitBounds(bounds, {
21034
+ padding: 50,
21035
+ maxZoom: 8
21036
+ });
21037
+ }
21038
+ });
21039
+ return () => {
21040
+ if (map.current) {
21041
+ map.current.remove();
21042
+ map.current = null;
21043
+ }
21044
+ };
21045
+ }, [projects, onProjectClick, mapConfig]);
21046
+ return /*#__PURE__*/jsxRuntime.jsx(Style$A, {
21047
+ children: /*#__PURE__*/jsxRuntime.jsx("div", {
21048
+ ref: mapContainer,
21049
+ style: {
21050
+ width: '100%',
21051
+ height: '100%'
21052
+ }
21053
+ })
21054
+ });
21055
+ };
21056
+
20839
21057
  function WidgetPlaceholder(_ref) {
20840
21058
  let {
20841
21059
  icon = "",
@@ -61609,6 +61827,7 @@ exports.SelectFiltersTimeFrame = Timeframe;
61609
61827
  exports.SettingsPopover = SettingsPopover;
61610
61828
  exports.Sidenav = Sidenav;
61611
61829
  exports.SidenavMenu = SidenavMenu;
61830
+ exports.SimpleGlobe = SimpleGlobe;
61612
61831
  exports.StackChart = StackChart;
61613
61832
  exports.StakeholderMappings = index$1;
61614
61833
  exports.Steps = DAFSteps;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "datastake-daf",
3
- "version": "0.6.265",
3
+ "version": "0.6.267",
4
4
  "dependencies": {
5
5
  "@ant-design/icons": "^5.2.5",
6
6
  "@antv/g2": "^5.1.1",
@@ -2,7 +2,7 @@ import Globe from "./index";
2
2
  import ThemeLayout from "../../ThemeLayout";
3
3
  import Widget from "../Widget";
4
4
 
5
- import * as configs from "../Map/storyConfig";
5
+ import * as configs from "./storyConfig";
6
6
 
7
7
  export default {
8
8
  title: "Dashboard/Globe",
@@ -29,14 +29,14 @@ export default {
29
29
  export const DefaultGlobe = {
30
30
  name: "Default Globe",
31
31
  args: {
32
- ...configs.DefaultMapConfig,
32
+ ...configs.DefaultGlobeConfig,
33
33
  },
34
34
  };
35
35
 
36
36
  export const SatelliteGlobe = {
37
37
  name: "Satellite Globe",
38
38
  args: {
39
- ...configs.DefaultMapConfig,
39
+ ...configs.DefaultGlobeConfig,
40
40
  isSatellite: true,
41
41
  },
42
42
  };
@@ -44,11 +44,19 @@ export const SatelliteGlobe = {
44
44
  export const TerritoryGlobe = {
45
45
  name: "Territory Globe",
46
46
  args: {
47
- ...configs.TerritoryMapConfig,
47
+ ...configs.TerritoryGlobeConfig,
48
48
  type: "territory",
49
49
  },
50
50
  };
51
51
 
52
+ export const StakeholderGlobe = {
53
+ name: "Stakeholder Globe",
54
+ args: {
55
+ ...configs.StakeholderGlobeConfig,
56
+ type: "stakeholder",
57
+ },
58
+ };
59
+
52
60
  export const EventGlobe = {
53
61
  name: "Event Globe",
54
62
  args: {
@@ -57,10 +65,27 @@ export const EventGlobe = {
57
65
  },
58
66
  };
59
67
 
68
+ export const ChainGlobe = {
69
+ name: "Supply Chain Globe",
70
+ args: {
71
+ ...configs.ChainGlobeConfig,
72
+ type: "chain",
73
+ },
74
+ };
75
+
60
76
  export const LocationGlobe = {
61
77
  name: "Location Globe",
62
78
  args: {
63
- ...configs.DefaultMapConfig,
79
+ ...configs.DefaultGlobeConfig,
64
80
  type: "location",
65
81
  },
66
82
  };
83
+
84
+ export const ProjectGlobe = {
85
+ name: "Project Globe",
86
+ args: {
87
+ ...configs.ProjectGlobeConfig,
88
+ type: "project",
89
+ },
90
+ };
91
+
@@ -0,0 +1,106 @@
1
+ # SimpleGlobe Component
2
+
3
+ A simplified wrapper for the Globe component that makes it easy to display project data as pins on a 3D globe.
4
+
5
+ ## Features
6
+
7
+ - πŸ—ΊοΈ **3D Globe Visualization**: Interactive 3D globe using Mapbox GL JS
8
+ - πŸ“ **Pin-based Display**: Projects are displayed as clickable pins on the globe
9
+ - 🎯 **Simple Integration**: Just pass your project data and it works
10
+ - πŸ“Š **Rich Tooltips**: Hover over pins to see project details
11
+ - 🎨 **Customizable**: Support for custom map configurations
12
+
13
+ ## Quick Start
14
+
15
+ ```jsx
16
+ import SimpleGlobe from './SimpleGlobe';
17
+
18
+ const MyComponent = () => {
19
+ const projects = [
20
+ {
21
+ name: "Solar Project",
22
+ latitude: 14.7167,
23
+ longitude: -17.4677,
24
+ percentageCompletion: 75,
25
+ projectDescription: "Large-scale solar energy project"
26
+ }
27
+ ];
28
+
29
+ return (
30
+ <div style={{ width: '100%', height: '600px' }}>
31
+ <SimpleGlobe
32
+ projects={projects}
33
+ onProjectClick={(project) => console.log('Clicked:', project)}
34
+ />
35
+ </div>
36
+ );
37
+ };
38
+ ```
39
+
40
+ ## Props
41
+
42
+ | Prop | Type | Default | Description |
43
+ |------|------|---------|-------------|
44
+ | `projects` | `Array` | `[]` | Array of project objects |
45
+ | `mapConfig` | `Object` | `{}` | Map configuration options |
46
+ | `showSider` | `boolean` | `false` | Whether to show the sidebar |
47
+ | `onProjectClick` | `Function` | `() => {}` | Callback when a project pin is clicked |
48
+
49
+ ## Project Data Format
50
+
51
+ Your project objects should have the following structure:
52
+
53
+ ```javascript
54
+ {
55
+ _id: "unique-id", // Optional: Unique identifier
56
+ name: "Project Name", // Required: Project name
57
+ latitude: 14.7167, // Required: Latitude coordinate
58
+ longitude: -17.4677, // Required: Longitude coordinate
59
+ projectDescription: "Description", // Optional: Project description
60
+ country: "SN", // Optional: Country code
61
+ sectoralScope: "energy", // Optional: Project sector
62
+ percentageCompletion: 75, // Optional: Completion percentage
63
+ author: { name: "Author Name" } // Optional: Author information
64
+ }
65
+ ```
66
+
67
+ ## Examples
68
+
69
+ ### Basic Usage
70
+ ```jsx
71
+ <SimpleGlobe projects={myProjects} />
72
+ ```
73
+
74
+ ### With Sidebar
75
+ ```jsx
76
+ <SimpleGlobe
77
+ projects={myProjects}
78
+ showSider={true}
79
+ />
80
+ ```
81
+
82
+ ### With Custom Configuration
83
+ ```jsx
84
+ <SimpleGlobe
85
+ projects={myProjects}
86
+ mapConfig={{
87
+ maxZoom: 8,
88
+ minZoom: 2
89
+ }}
90
+ onProjectClick={(project) => {
91
+ // Handle project click
92
+ console.log('Project clicked:', project);
93
+ }}
94
+ />
95
+ ```
96
+
97
+ ## Storybook
98
+
99
+ You can see all examples in Storybook under "Dashboard/Globe/SimpleGlobe".
100
+
101
+ ## Notes
102
+
103
+ - The component automatically transforms your project data to the format expected by the underlying Globe component
104
+ - Pins are displayed as circular markers with project completion percentage
105
+ - Tooltips show project details when hovering over pins
106
+ - The globe is fully interactive - you can zoom, pan, and rotate