RoyAalekh commited on
Commit
2150b01
·
1 Parent(s): ee992c9

Fix map UI/UX issues: proper SVG pins, remove emojis, improve tooltips and performance

Browse files

- Replace emoji pins with professional SVG markers that properly anchor to map
- Remove View button from popups - tooltip already shows tree info
- Remove ALL emojis for professional appearance
- Fix pin floating issue with proper iconAnchor configuration
- Larger, more visible pins (28x36px for trees, 24x32px for temp/user)
- Enhanced tooltip with creator info and better formatting
- Simplified popup content with cleaner layout
- Performance optimizations with debounced map fitting
- Professional color-coded markers (green=trees, red=temp, blue=user)
- Smooth hover animations and proper drop shadows
- Fixed redirect to main form page instead of static path

Files changed (2) hide show
  1. static/map.html +91 -11
  2. static/map.js +81 -171
static/map.html CHANGED
@@ -664,6 +664,86 @@
664
  25%, 75% { opacity: 1; }
665
  }
666
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
667
  /* Focus States */
668
  .btn:focus-visible,
669
  .control-button:focus-visible,
@@ -693,17 +773,17 @@
693
  <div class="header">
694
  <div class="header-content">
695
  <div class="header-brand">
696
- <div class="logo">🌳 TreeTrack Map</div>
697
  </div>
698
 
699
  <div class="header-stats">
700
  <div class="stat-item">
701
- <span class="stat-icon">🌲</span>
702
  <span class="stat-text">Trees:</span>
703
  <span class="stat-number" id="treeCount">0</span>
704
  </div>
705
  <div class="stat-item">
706
- <span class="stat-icon">📍</span>
707
  <span class="stat-text">Selected:</span>
708
  <span class="stat-number" id="selectedCount">0</span>
709
  </div>
@@ -717,7 +797,7 @@
717
  <div class="user-role" id="userRole">User</div>
718
  </div>
719
  </div>
720
- <a href="/" class="btn btn-secondary">✏️ Add Tree</a>
721
  <button id="logoutBtn" class="btn btn-secondary">Logout</button>
722
  </div>
723
  </div>
@@ -735,15 +815,15 @@
735
  <div class="controls-content">
736
  <div class="control-group">
737
  <button id="myLocationBtn" class="control-button">
738
- <span>📍</span>
739
  <span>My Location</span>
740
  </button>
741
  <button id="clearPinsBtn" class="control-button">
742
- <span>🧹</span>
743
  <span>Clear Pins</span>
744
  </button>
745
  <button id="centerMapBtn" class="control-button">
746
- <span>🎯</span>
747
  <span>Center View</span>
748
  </button>
749
  </div>
@@ -753,7 +833,7 @@
753
  <!-- Location Panel -->
754
  <div class="location-panel" id="locationPanel">
755
  <div class="location-header">
756
- <h3 class="location-title">📍 Location Selected</h3>
757
  </div>
758
  <div class="location-content">
759
  <div class="coordinates-grid">
@@ -768,10 +848,10 @@
768
  </div>
769
  <div class="location-actions">
770
  <button id="useLocationBtn" class="btn btn-primary">
771
- Use This Location
772
  </button>
773
  <button id="cancelBtn" class="btn btn-secondary">
774
- Cancel
775
  </button>
776
  </div>
777
  </div>
@@ -788,7 +868,7 @@
788
 
789
  <!-- Gesture Hint -->
790
  <div class="gesture-hint">
791
- 💡 Tap anywhere on the map to drop a location pin
792
  </div>
793
  </div>
794
  </div>
 
664
  25%, 75% { opacity: 1; }
665
  }
666
 
667
+ /* Custom Map Markers */
668
+ .map-marker {
669
+ position: relative;
670
+ display: flex;
671
+ align-items: center;
672
+ justify-content: center;
673
+ filter: drop-shadow(0 2px 4px rgba(0, 0, 0, 0.2));
674
+ transition: transform 0.2s ease, filter 0.2s ease;
675
+ }
676
+
677
+ .map-marker:hover {
678
+ transform: scale(1.1);
679
+ filter: drop-shadow(0 4px 8px rgba(0, 0, 0, 0.3));
680
+ }
681
+
682
+ .tree-marker svg {
683
+ filter: drop-shadow(0 1px 3px rgba(0, 0, 0, 0.3));
684
+ }
685
+
686
+ .temp-marker svg {
687
+ animation: pulse-marker 2s infinite;
688
+ }
689
+
690
+ .user-marker svg {
691
+ filter: drop-shadow(0 2px 6px rgba(59, 130, 246, 0.5));
692
+ }
693
+
694
+ @keyframes pulse-marker {
695
+ 0%, 100% {
696
+ opacity: 1;
697
+ transform: scale(1);
698
+ }
699
+ 50% {
700
+ opacity: 0.8;
701
+ transform: scale(1.05);
702
+ }
703
+ }
704
+
705
+ /* Tree Tooltip Improvements */
706
+ .tree-tooltip {
707
+ background: rgba(255, 255, 255, 0.95) !important;
708
+ border: 1px solid var(--gray-200) !important;
709
+ border-radius: var(--radius-md) !important;
710
+ box-shadow: var(--shadow-lg) !important;
711
+ backdrop-filter: blur(8px);
712
+ font-size: 0.8125rem;
713
+ padding: var(--space-2) var(--space-3) !important;
714
+ max-width: 200px;
715
+ }
716
+
717
+ .tree-tooltip-content {
718
+ text-align: center;
719
+ }
720
+
721
+ .tree-name {
722
+ font-weight: 600;
723
+ color: var(--gray-900);
724
+ margin-bottom: var(--space-1);
725
+ }
726
+
727
+ .tree-details {
728
+ font-size: 0.75rem;
729
+ color: var(--gray-600);
730
+ }
731
+
732
+ .tree-creator {
733
+ font-size: 0.7rem;
734
+ color: var(--gray-500);
735
+ font-style: italic;
736
+ }
737
+
738
+ /* Performance optimizations */
739
+ .leaflet-tile-pane {
740
+ filter: none;
741
+ }
742
+
743
+ .leaflet-overlay-pane {
744
+ transform: translate3d(0, 0, 0);
745
+ }
746
+
747
  /* Focus States */
748
  .btn:focus-visible,
749
  .control-button:focus-visible,
 
773
  <div class="header">
774
  <div class="header-content">
775
  <div class="header-brand">
776
+ <div class="logo">TreeTrack Map</div>
777
  </div>
778
 
779
  <div class="header-stats">
780
  <div class="stat-item">
781
+ <span class="stat-icon">T</span>
782
  <span class="stat-text">Trees:</span>
783
  <span class="stat-number" id="treeCount">0</span>
784
  </div>
785
  <div class="stat-item">
786
+ <span class="stat-icon">S</span>
787
  <span class="stat-text">Selected:</span>
788
  <span class="stat-number" id="selectedCount">0</span>
789
  </div>
 
797
  <div class="user-role" id="userRole">User</div>
798
  </div>
799
  </div>
800
+ <a href="/" class="btn btn-secondary">Add Tree</a>
801
  <button id="logoutBtn" class="btn btn-secondary">Logout</button>
802
  </div>
803
  </div>
 
815
  <div class="controls-content">
816
  <div class="control-group">
817
  <button id="myLocationBtn" class="control-button">
818
+ <span>Loc</span>
819
  <span>My Location</span>
820
  </button>
821
  <button id="clearPinsBtn" class="control-button">
822
+ <span>Clr</span>
823
  <span>Clear Pins</span>
824
  </button>
825
  <button id="centerMapBtn" class="control-button">
826
+ <span>Ctr</span>
827
  <span>Center View</span>
828
  </button>
829
  </div>
 
833
  <!-- Location Panel -->
834
  <div class="location-panel" id="locationPanel">
835
  <div class="location-header">
836
+ <h3 class="location-title">Location Selected</h3>
837
  </div>
838
  <div class="location-content">
839
  <div class="coordinates-grid">
 
848
  </div>
849
  <div class="location-actions">
850
  <button id="useLocationBtn" class="btn btn-primary">
851
+ Use This Location
852
  </button>
853
  <button id="cancelBtn" class="btn btn-secondary">
854
+ Cancel
855
  </button>
856
  </div>
857
  </div>
 
868
 
869
  <!-- Gesture Hint -->
870
  <div class="gesture-hint">
871
+ Tap anywhere on the map to drop a location pin
872
  </div>
873
  </div>
874
  </div>
static/map.js CHANGED
@@ -260,20 +260,20 @@ class TreeTrackMap {
260
  // Remove existing temp marker
261
  this.clearTempMarker();
262
 
263
- // Create new temp marker with glow effect
264
  const tempIcon = L.divIcon({
265
  html: `
266
- <div class="custom-tree-marker">
267
- <div class="tree-icon-container temp-pin">
268
- 📍
269
- </div>
270
- <div class="tree-marker-shadow"></div>
271
  </div>
272
  `,
273
- className: 'custom-tree-icon',
274
- iconSize: [32, 42],
275
- iconAnchor: [16, 42],
276
- popupAnchor: [0, -42]
277
  });
278
 
279
  this.tempMarker = L.marker([e.latlng.lat, e.latlng.lng], { icon: tempIcon }).addTo(this.map);
@@ -325,13 +325,13 @@ class TreeTrackMap {
325
  this.showMessage('Location saved! Redirecting to form...', 'success');
326
 
327
  setTimeout(() => {
328
- window.location.href = '/static/index.html';
329
- }, 1500);
330
  }
331
 
332
  cancelLocationSelection() {
333
  this.clearTempMarker();
334
- this.showMessage('Selection cancelled', 'success');
335
  }
336
 
337
  centerMapToTrees() {
@@ -343,7 +343,7 @@ class TreeTrackMap {
343
  // Fit map to show all trees
344
  const group = new L.featureGroup(this.treeMarkers);
345
  this.map.fitBounds(group.getBounds().pad(0.1));
346
- this.showMessage('Centered map on all trees', 'success');
347
  }
348
 
349
  getCurrentLocation() {
@@ -377,17 +377,17 @@ class TreeTrackMap {
377
 
378
  const userIcon = L.divIcon({
379
  html: `
380
- <div class="custom-tree-marker">
381
- <div class="tree-icon-container" style="background: linear-gradient(145deg, #3b82f6, #1d4ed8);">
382
- 🫵
383
- </div>
384
- <div class="tree-marker-shadow"></div>
385
  </div>
386
  `,
387
- className: 'custom-tree-icon',
388
- iconSize: [32, 42],
389
- iconAnchor: [16, 42],
390
- popupAnchor: [0, -42]
391
  });
392
 
393
  this.userLocationMarker = L.marker([lat, lng], { icon: userIcon }).addTo(this.map);
@@ -400,7 +400,7 @@ class TreeTrackMap {
400
  className: 'tree-tooltip'
401
  });
402
 
403
- this.showMessage('📍 Location found!', 'success');
404
 
405
  myLocationBtn.textContent = 'My Location';
406
  myLocationBtn.disabled = false;
@@ -454,11 +454,13 @@ class TreeTrackMap {
454
  // Update tree count
455
  document.getElementById('treeCount').textContent = trees.length;
456
 
457
- if (trees.length > 0) {
458
- // Fit map to show all trees
 
459
  const group = new L.featureGroup(this.treeMarkers);
460
  this.map.fitBounds(group.getBounds().pad(0.1));
461
- }
 
462
 
463
  } catch (error) {
464
  console.error('Error loading trees:', error);
@@ -469,17 +471,17 @@ class TreeTrackMap {
469
  addTreeMarker(tree) {
470
  const treeIcon = L.divIcon({
471
  html: `
472
- <div class="custom-tree-marker">
473
- <div class="tree-icon-container tree-pin">
474
- 🌳
475
- </div>
476
- <div class="tree-marker-shadow"></div>
477
  </div>
478
  `,
479
- className: 'custom-tree-icon',
480
- iconSize: [32, 42],
481
- iconAnchor: [16, 42],
482
- popupAnchor: [0, -42]
483
  });
484
 
485
  const marker = L.marker([tree.latitude, tree.longitude], { icon: treeIcon }).addTo(this.map);
@@ -489,7 +491,8 @@ class TreeTrackMap {
489
  const tooltipContent = `
490
  <div class="tree-tooltip-content">
491
  <div class="tree-name">${treeName}</div>
492
- <div class="tree-details">ID: ${tree.id}${tree.tree_code ? ' ' + tree.tree_code : ''}</div>
 
493
  </div>
494
  `;
495
 
@@ -500,62 +503,63 @@ class TreeTrackMap {
500
  className: 'tree-tooltip'
501
  });
502
 
503
- // Enhanced popup with action buttons
504
  const canEdit = this.canEditTree(tree.created_by);
505
  const canDelete = this.canDeleteTree(tree.created_by);
506
 
507
  const popupContent = `
508
- <div style="min-width: 280px; font-family: 'Segoe UI', sans-serif;">
509
- <div style="border-bottom: 1px solid #e5e7eb; padding-bottom: 12px; margin-bottom: 12px;">
510
- <h3 style="margin: 0 0 8px 0; color: #1e40af; font-size: 16px; font-weight: 600;">
511
  ${treeName}
512
  </h3>
513
- <div style="color: #6b7280; font-size: 13px;">
514
  <strong>Tree ID:</strong> #${tree.id}${tree.tree_code ? ' (' + tree.tree_code + ')' : ''}
515
  </div>
516
  </div>
517
 
518
- <div style="display: grid; grid-template-columns: 1fr 1fr; gap: 8px; margin-bottom: 12px; font-size: 13px;">
519
- <div><strong>📍 Location:</strong></div>
520
- <div>${tree.latitude.toFixed(4)}, ${tree.longitude.toFixed(4)}</div>
521
-
522
- ${tree.height ? `<div><strong>📏 Height:</strong></div><div>${tree.height}m</div>` : ''}
523
- ${tree.width ? `<div><strong>📐 Girth:</strong></div><div>${tree.width}cm</div>` : ''}
524
-
525
- <div><strong>👤 Added by:</strong></div>
526
- <div>${tree.created_by || 'Unknown'}</div>
527
-
528
- <div><strong>📅 Date:</strong></div>
529
- <div>${new Date(tree.created_at).toLocaleDateString()}</div>
530
  </div>
531
 
532
  ${tree.notes ? `
533
- <div style="margin-bottom: 12px;">
534
- <strong style="color: #374151; font-size: 13px;">📝 Notes:</strong>
535
- <div style="color: #6b7280; font-size: 12px; margin-top: 4px; line-height: 1.4;">
536
- ${tree.notes.substring(0, 120)}${tree.notes.length > 120 ? '...' : ''}
537
  </div>
538
  </div>
539
  ` : ''}
540
 
541
- <div style="display: flex; gap: 8px; margin-top: 12px; padding-top: 12px; border-top: 1px solid #e5e7eb;">
542
- ${canEdit ? `
543
- <button onclick="mapApp.editTree(${tree.id})"
544
- style="flex: 1; background: linear-gradient(145deg, #3b82f6, #2563eb); color: white; border: none; padding: 8px 12px; border-radius: 6px; cursor: pointer; font-size: 12px; font-weight: 600; transition: all 0.2s;">
545
- ✏️ Edit
546
- </button>
547
- ` : ''}
548
- ${canDelete ? `
549
- <button onclick="mapApp.deleteTree(${tree.id})"
550
- style="flex: 1; background: linear-gradient(145deg, #ef4444, #dc2626); color: white; border: none; padding: 8px 12px; border-radius: 6px; cursor: pointer; font-size: 12px; font-weight: 600; transition: all 0.2s;">
551
- 🗑️ Delete
552
- </button>
553
- ` : ''}
554
- <button onclick="mapApp.viewTreeDetails(${tree.id})"
555
- style="flex: 1; background: linear-gradient(145deg, #059669, #047857); color: white; border: none; padding: 8px 12px; border-radius: 6px; cursor: pointer; font-size: 12px; font-weight: 600; transition: all 0.2s;">
556
- 👁️ View
557
- </button>
558
- </div>
 
 
559
  </div>
560
  `;
561
 
@@ -613,7 +617,7 @@ class TreeTrackMap {
613
  if (!response) return;
614
 
615
  if (response.ok) {
616
- this.showMessage(`🗑️ Tree #${treeId} deleted successfully.`, 'success');
617
 
618
  // Reload trees to update the map
619
  setTimeout(() => {
@@ -633,100 +637,6 @@ class TreeTrackMap {
633
  }
634
  }
635
 
636
- async viewTreeDetails(treeId) {
637
- try {
638
- const response = await this.authenticatedFetch(`/api/trees/${treeId}`);
639
- if (!response) return;
640
-
641
- if (!response.ok) {
642
- throw new Error('Failed to fetch tree data');
643
- }
644
-
645
- const tree = await response.json();
646
-
647
- // Create detailed view popup
648
- const detailContent = `
649
- <div style="max-width: 350px; font-family: 'Segoe UI', sans-serif;">
650
- <div style="border-bottom: 2px solid #1e40af; padding-bottom: 12px; margin-bottom: 16px;">
651
- <h2 style="margin: 0; color: #1e40af; font-size: 18px; font-weight: 700;">
652
- 🌳 ${tree.scientific_name || tree.common_name || tree.local_name || 'Unknown Tree'}
653
- </h2>
654
- <div style="color: #6b7280; font-size: 14px; margin-top: 4px;">
655
- Tree #${tree.id}${tree.tree_code ? ' • Code: ' + tree.tree_code : ''}
656
- </div>
657
- </div>
658
-
659
- <div style="margin-bottom: 16px;">
660
- <div style="display: grid; grid-template-columns: auto 1fr; gap: 8px 16px; font-size: 13px;">
661
- ${tree.local_name ? `<strong>🏷️ Local Name:</strong><span>${tree.local_name}</span>` : ''}
662
- ${tree.scientific_name ? `<strong>🔬 Scientific:</strong><span><em>${tree.scientific_name}</em></span>` : ''}
663
- ${tree.common_name ? `<strong>🌍 Common:</strong><span>${tree.common_name}</span>` : ''}
664
- ${tree.height ? `<strong>📏 Height:</strong><span>${tree.height} meters</span>` : ''}
665
- ${tree.width ? `<strong>📐 Girth:</strong><span>${tree.width} cm</span>` : ''}
666
- <strong>📍 Coordinates:</strong><span>${tree.latitude.toFixed(6)}, ${tree.longitude.toFixed(6)}</span>
667
- <strong>👤 Created by:</strong><span>${tree.created_by || 'Unknown'}</span>
668
- <strong>📅 Date:</strong><span>${new Date(tree.created_at).toLocaleDateString()}</span>
669
- </div>
670
- </div>
671
-
672
- ${tree.utility && tree.utility.length > 0 ? `
673
- <div style="margin-bottom: 12px;">
674
- <strong style="color: #059669; font-size: 14px;">🌿 Ecological Uses:</strong>
675
- <div style="margin-top: 4px;">
676
- ${tree.utility.map(u => `<span style="display: inline-block; background: #dcfce7; color: #166534; padding: 2px 6px; border-radius: 4px; font-size: 11px; margin: 2px;">${u}</span>`).join('')}
677
- </div>
678
- </div>
679
- ` : ''}
680
-
681
- ${tree.phenology_stages && tree.phenology_stages.length > 0 ? `
682
- <div style="margin-bottom: 12px;">
683
- <strong style="color: #7c3aed; font-size: 14px;">🌸 Current Stages:</strong>
684
- <div style="margin-top: 4px;">
685
- ${tree.phenology_stages.map(stage => `<span style="display: inline-block; background: #ede9fe; color: #6b21a8; padding: 2px 6px; border-radius: 4px; font-size: 11px; margin: 2px;">${stage}</span>`).join('')}
686
- </div>
687
- </div>
688
- ` : ''}
689
-
690
- ${tree.storytelling_text ? `
691
- <div style="margin-bottom: 12px;">
692
- <strong style="color: #ea580c; font-size: 14px;">📖 Stories & Culture:</strong>
693
- <div style="color: #6b7280; font-size: 12px; margin-top: 4px; line-height: 1.4; max-height: 80px; overflow-y: auto; padding: 8px; background: #f9fafb; border-radius: 6px;">
694
- ${tree.storytelling_text}
695
- </div>
696
- </div>
697
- ` : ''}
698
-
699
- ${tree.notes ? `
700
- <div style="margin-bottom: 12px;">
701
- <strong style="color: #374151; font-size: 14px;">📝 Notes:</strong>
702
- <div style="color: #6b7280; font-size: 12px; margin-top: 4px; line-height: 1.4; padding: 8px; background: #f9fafb; border-radius: 6px;">
703
- ${tree.notes}
704
- </div>
705
- </div>
706
- ` : ''}
707
- </div>
708
- `;
709
-
710
- // Close current popup and show detailed one
711
- this.map.closePopup();
712
-
713
- // Find the marker for this tree and open detailed popup
714
- const treeMarker = this.treeMarkers.find(marker => {
715
- return marker.getLatLng().lat === tree.latitude && marker.getLatLng().lng === tree.longitude;
716
- });
717
-
718
- if (treeMarker) {
719
- treeMarker.bindPopup(detailContent, {
720
- maxWidth: 400,
721
- className: 'tree-popup'
722
- }).openPopup();
723
- }
724
-
725
- } catch (error) {
726
- console.error('Error viewing tree details:', error);
727
- this.showMessage('Error loading tree details: ' + error.message, 'error');
728
- }
729
- }
730
 
731
  showLoading() {
732
  document.getElementById('loading').style.display = 'block';
 
260
  // Remove existing temp marker
261
  this.clearTempMarker();
262
 
263
+ // Create new temp marker with proper SVG icon
264
  const tempIcon = L.divIcon({
265
  html: `
266
+ <div class="map-marker temp-marker">
267
+ <svg width="24" height="32" viewBox="0 0 24 32" fill="none">
268
+ <path d="M12 0C5.37 0 0 5.37 0 12C0 21 12 32 12 32S24 21 24 12C24 5.37 18.63 0 12 0Z" fill="#ef4444"/>
269
+ <circle cx="12" cy="12" r="4" fill="white"/>
270
+ </svg>
271
  </div>
272
  `,
273
+ className: 'custom-marker-icon',
274
+ iconSize: [24, 32],
275
+ iconAnchor: [12, 32],
276
+ popupAnchor: [0, -32]
277
  });
278
 
279
  this.tempMarker = L.marker([e.latlng.lat, e.latlng.lng], { icon: tempIcon }).addTo(this.map);
 
325
  this.showMessage('Location saved! Redirecting to form...', 'success');
326
 
327
  setTimeout(() => {
328
+ window.location.href = '/';
329
+ }, 1000);
330
  }
331
 
332
  cancelLocationSelection() {
333
  this.clearTempMarker();
334
+ this.showMessage('Selection cancelled', 'info');
335
  }
336
 
337
  centerMapToTrees() {
 
343
  // Fit map to show all trees
344
  const group = new L.featureGroup(this.treeMarkers);
345
  this.map.fitBounds(group.getBounds().pad(0.1));
346
+ this.showMessage('Map centered on all trees', 'success');
347
  }
348
 
349
  getCurrentLocation() {
 
377
 
378
  const userIcon = L.divIcon({
379
  html: `
380
+ <div class="map-marker user-marker">
381
+ <svg width="24" height="32" viewBox="0 0 24 32" fill="none">
382
+ <path d="M12 0C5.37 0 0 5.37 0 12C0 21 12 32 12 32S24 21 24 12C24 5.37 18.63 0 12 0Z" fill="#3b82f6"/>
383
+ <circle cx="12" cy="12" r="4" fill="white"/>
384
+ </svg>
385
  </div>
386
  `,
387
+ className: 'custom-marker-icon',
388
+ iconSize: [24, 32],
389
+ iconAnchor: [12, 32],
390
+ popupAnchor: [0, -32]
391
  });
392
 
393
  this.userLocationMarker = L.marker([lat, lng], { icon: userIcon }).addTo(this.map);
 
400
  className: 'tree-tooltip'
401
  });
402
 
403
+ this.showMessage('Location found successfully', 'success');
404
 
405
  myLocationBtn.textContent = 'My Location';
406
  myLocationBtn.disabled = false;
 
454
  // Update tree count
455
  document.getElementById('treeCount').textContent = trees.length;
456
 
457
+ if (trees.length > 0) {
458
+ // Fit map to show all trees with debounced execution
459
+ setTimeout(() => {
460
  const group = new L.featureGroup(this.treeMarkers);
461
  this.map.fitBounds(group.getBounds().pad(0.1));
462
+ }, 100);
463
+ }
464
 
465
  } catch (error) {
466
  console.error('Error loading trees:', error);
 
471
  addTreeMarker(tree) {
472
  const treeIcon = L.divIcon({
473
  html: `
474
+ <div class="map-marker tree-marker">
475
+ <svg width="28" height="36" viewBox="0 0 24 32" fill="none">
476
+ <path d="M12 0C5.37 0 0 5.37 0 12C0 21 12 32 12 32S24 21 24 12C24 5.37 18.63 0 12 0Z" fill="#22c55e"/>
477
+ <circle cx="12" cy="12" r="4" fill="white"/>
478
+ </svg>
479
  </div>
480
  `,
481
+ className: 'custom-marker-icon',
482
+ iconSize: [28, 36],
483
+ iconAnchor: [14, 36],
484
+ popupAnchor: [0, -36]
485
  });
486
 
487
  const marker = L.marker([tree.latitude, tree.longitude], { icon: treeIcon }).addTo(this.map);
 
491
  const tooltipContent = `
492
  <div class="tree-tooltip-content">
493
  <div class="tree-name">${treeName}</div>
494
+ <div class="tree-details">ID: ${tree.id}${tree.tree_code ? ' | ' + tree.tree_code : ''}</div>
495
+ ${tree.created_by ? `<div class="tree-creator">by ${tree.created_by}</div>` : ''}
496
  </div>
497
  `;
498
 
 
503
  className: 'tree-tooltip'
504
  });
505
 
506
+ // Enhanced popup with action buttons (no emojis)
507
  const canEdit = this.canEditTree(tree.created_by);
508
  const canDelete = this.canDeleteTree(tree.created_by);
509
 
510
  const popupContent = `
511
+ <div style="min-width: 320px; font-family: 'Segoe UI', sans-serif;">
512
+ <div style="border-bottom: 1px solid #e5e7eb; padding-bottom: 16px; margin-bottom: 16px;">
513
+ <h3 style="margin: 0 0 8px 0; color: #1e40af; font-size: 18px; font-weight: 600;">
514
  ${treeName}
515
  </h3>
516
+ <div style="color: #6b7280; font-size: 14px;">
517
  <strong>Tree ID:</strong> #${tree.id}${tree.tree_code ? ' (' + tree.tree_code + ')' : ''}
518
  </div>
519
  </div>
520
 
521
+ <div style="margin-bottom: 16px;">
522
+ <div style="display: grid; grid-template-columns: auto 1fr; gap: 8px 16px; font-size: 14px;">
523
+ ${tree.local_name ? `<strong>Local Name:</strong><span>${tree.local_name}</span>` : ''}
524
+ ${tree.scientific_name ? `<strong>Scientific:</strong><span><em>${tree.scientific_name}</em></span>` : ''}
525
+ ${tree.common_name ? `<strong>Common:</strong><span>${tree.common_name}</span>` : ''}
526
+ <strong>Location:</strong><span>${tree.latitude.toFixed(6)}, ${tree.longitude.toFixed(6)}</span>
527
+ ${tree.height ? `<strong>Height:</strong><span>${tree.height} meters</span>` : ''}
528
+ ${tree.width ? `<strong>Girth:</strong><span>${tree.width} cm</span>` : ''}
529
+ <strong>Added by:</strong><span>${tree.created_by || 'Unknown'}</span>
530
+ <strong>Date:</strong><span>${new Date(tree.created_at).toLocaleDateString()}</span>
531
+ </div>
 
532
  </div>
533
 
534
  ${tree.notes ? `
535
+ <div style="margin-bottom: 16px; padding: 12px; background: #f9fafb; border-radius: 8px;">
536
+ <strong style="color: #374151; font-size: 14px; display: block; margin-bottom: 8px;">Notes:</strong>
537
+ <div style="color: #6b7280; font-size: 13px; line-height: 1.5;">
538
+ ${tree.notes}
539
  </div>
540
  </div>
541
  ` : ''}
542
 
543
+ ${canEdit || canDelete ? `
544
+ <div style="display: flex; gap: 8px; margin-top: 16px; padding-top: 16px; border-top: 1px solid #e5e7eb;">
545
+ ${canEdit ? `
546
+ <button onclick="mapApp.editTree(${tree.id})"
547
+ style="flex: 1; background: #3b82f6; color: white; border: none; padding: 10px 16px; border-radius: 8px; cursor: pointer; font-size: 14px; font-weight: 600; transition: background-color 0.2s;"
548
+ onmouseover="this.style.backgroundColor='#2563eb'"
549
+ onmouseout="this.style.backgroundColor='#3b82f6'">
550
+ Edit Tree
551
+ </button>
552
+ ` : ''}
553
+ ${canDelete ? `
554
+ <button onclick="mapApp.deleteTree(${tree.id})"
555
+ style="flex: 1; background: #ef4444; color: white; border: none; padding: 10px 16px; border-radius: 8px; cursor: pointer; font-size: 14px; font-weight: 600; transition: background-color 0.2s;"
556
+ onmouseover="this.style.backgroundColor='#dc2626'"
557
+ onmouseout="this.style.backgroundColor='#ef4444'">
558
+ Delete Tree
559
+ </button>
560
+ ` : ''}
561
+ </div>
562
+ ` : ''}
563
  </div>
564
  `;
565
 
 
617
  if (!response) return;
618
 
619
  if (response.ok) {
620
+ this.showMessage(`Tree #${treeId} deleted successfully`, 'success');
621
 
622
  // Reload trees to update the map
623
  setTimeout(() => {
 
637
  }
638
  }
639
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
640
 
641
  showLoading() {
642
  document.getElementById('loading').style.display = 'block';