RoyAalekh commited on
Commit
a662e75
·
1 Parent(s): a2ad91d

Enhance TreeTrack map with beautiful tree pins and fixed popup modals

Browse files

Visual Enhancements:
- Replaced basic pins with detailed tree-shaped SVG icons
- Added red temporary pins with layered tree design and drop shadows
- Upgraded existing tree markers with 3D green tree icons
- Increased pin sizes for better visibility (32x40, 36x44 pixels)

Popup Modal Fixes:
- Fixed button overflow and layout issues
- Properly positioned close button with full functionality
- Improved responsive design (300px width, max 90vw)
- Enhanced typography with better spacing and font sizes
- Added smooth button hover animations and visual effects

User Experience Improvements:
- Fixed 'no location found' message handling
- Better message clearing on form redirect
- Added keyboard support (ESC to close popups)
- Improved word wrapping for long tree names
- Professional styling with consistent spacing

All map functionality now works seamlessly with enhanced visual appeal.

Files changed (1) hide show
  1. static/map.js +106 -68
static/map.js CHANGED
@@ -260,20 +260,31 @@ class TreeTrackMap {
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);
@@ -322,6 +333,9 @@ class TreeTrackMap {
322
  lng: this.selectedLocation.lng
323
  }));
324
 
 
 
 
325
  this.showMessage('Location saved! Redirecting to form...', 'success');
326
 
327
  setTimeout(() => {
@@ -471,17 +485,32 @@ class TreeTrackMap {
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);
@@ -508,64 +537,73 @@ class TreeTrackMap {
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
 
566
  marker.bindPopup(popupContent, {
567
- maxWidth: 300,
568
- className: 'tree-popup'
 
 
 
 
 
569
  });
570
 
571
  this.treeMarkers.push(marker);
 
260
  // Remove existing temp marker
261
  this.clearTempMarker();
262
 
263
+ // Create beautiful tree-shaped temp marker
264
  const tempIcon = L.divIcon({
265
  html: `
266
+ <div class="map-marker temp-marker" style="filter: drop-shadow(2px 2px 4px rgba(0,0,0,0.3));">
267
+ <svg width="32" height="40" viewBox="0 0 32 40" fill="none">
268
+ <!-- Tree Pin Base -->
269
+ <path d="M16 0C9.37 0 4 5.37 4 12C4 21 16 40 16 40S28 21 28 12C28 5.37 22.63 0 16 0Z" fill="#dc2626" stroke="#991b1b" stroke-width="1"/>
270
+ <!-- Tree Icon Inside -->
271
+ <g transform="translate(16, 12)">
272
+ <!-- Tree trunk -->
273
+ <rect x="-1" y="3" width="2" height="4" fill="#8b5a2b"/>
274
+ <!-- Tree crown layers -->
275
+ <circle cx="0" cy="0" r="4" fill="#22c55e"/>
276
+ <circle cx="-1" cy="-1" r="3" fill="#16a34a"/>
277
+ <circle cx="1" cy="1" r="2.5" fill="#15803d"/>
278
+ <!-- Highlight -->
279
+ <circle cx="-1.5" cy="-2" r="1" fill="#4ade80" opacity="0.6"/>
280
+ </g>
281
  </svg>
282
  </div>
283
  `,
284
+ className: 'custom-marker-icon tree-pin-temp',
285
+ iconSize: [32, 40],
286
+ iconAnchor: [16, 40],
287
+ popupAnchor: [0, -40]
288
  });
289
 
290
  this.tempMarker = L.marker([e.latlng.lat, e.latlng.lng], { icon: tempIcon }).addTo(this.map);
 
333
  lng: this.selectedLocation.lng
334
  }));
335
 
336
+ // Clear the message and redirect
337
+ const messageElement = document.getElementById('message');
338
+ if(messageElement) messageElement.classList.remove('show');
339
  this.showMessage('Location saved! Redirecting to form...', 'success');
340
 
341
  setTimeout(() => {
 
485
  addTreeMarker(tree) {
486
  const treeIcon = L.divIcon({
487
  html: `
488
+ <div class="map-marker tree-marker" style="filter: drop-shadow(1px 1px 3px rgba(0,0,0,0.4));">
489
+ <svg width="36" height="44" viewBox="0 0 36 44" fill="none">
490
+ <!-- Tree Pin Base -->
491
+ <path d="M18 0C11.37 0 6 5.37 6 12C6 21 18 44 18 44S30 21 30 12C30 5.37 24.63 0 18 0Z" fill="#16a34a" stroke="#15803d" stroke-width="1"/>
492
+ <!-- Tree Icon Inside -->
493
+ <g transform="translate(18, 14)">
494
+ <!-- Tree trunk -->
495
+ <rect x="-1.5" y="4" width="3" height="5" fill="#8b5a2b" rx="1"/>
496
+ <!-- Tree crown - main layer -->
497
+ <circle cx="0" cy="0" r="5" fill="#22c55e"/>
498
+ <!-- Tree crown - shadow layer -->
499
+ <circle cx="-1" cy="-1" r="4" fill="#16a34a"/>
500
+ <!-- Tree crown - highlight layer -->
501
+ <circle cx="1" cy="1" r="3" fill="#15803d"/>
502
+ <!-- Small highlight for 3D effect -->
503
+ <circle cx="-2" cy="-2.5" r="1.2" fill="#4ade80" opacity="0.7"/>
504
+ <!-- Tiny highlight -->
505
+ <circle cx="-2.5" cy="-3" r="0.5" fill="#86efac" opacity="0.8"/>
506
+ </g>
507
  </svg>
508
  </div>
509
  `,
510
+ className: 'custom-marker-icon tree-pin',
511
+ iconSize: [36, 44],
512
+ iconAnchor: [18, 44],
513
+ popupAnchor: [0, -44]
514
  });
515
 
516
  const marker = L.marker([tree.latitude, tree.longitude], { icon: treeIcon }).addTo(this.map);
 
537
  const canDelete = this.canDeleteTree(tree.created_by);
538
 
539
  const popupContent = `
540
+ <div style="width: 300px; max-width: 90vw; font-family: 'Segoe UI', sans-serif; position: relative;">
541
+ <div style="padding: 20px; padding-bottom: 16px;">
542
+ <div style="display: flex; justify-content: between; align-items: flex-start; margin-bottom: 16px;">
543
+ <div style="flex: 1;">
544
+ <h3 style="margin: 0 0 8px 0; color: #1e40af; font-size: 18px; font-weight: 600; line-height: 1.2; word-wrap: break-word;">
545
+ ${treeName}
546
+ </h3>
547
+ <div style="color: #6b7280; font-size: 13px;">
548
+ <strong>Tree ID:</strong> #${tree.id}${tree.tree_code ? ' (' + tree.tree_code + ')' : ''}
549
+ </div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
550
  </div>
551
  </div>
552
+
553
+ <div style="margin-bottom: 16px;">
554
+ <div style="display: grid; grid-template-columns: auto 1fr; gap: 6px 12px; font-size: 13px;">
555
+ ${tree.local_name ? `<strong style="color: #374151;">Local:</strong><span style="word-wrap: break-word;">${tree.local_name}</span>` : ''}
556
+ ${tree.scientific_name ? `<strong style="color: #374151;">Scientific:</strong><span style="word-wrap: break-word;"><em>${tree.scientific_name}</em></span>` : ''}
557
+ ${tree.common_name ? `<strong style="color: #374151;">Common:</strong><span style="word-wrap: break-word;">${tree.common_name}</span>` : ''}
558
+ <strong style="color: #374151;">Location:</strong><span style="font-family: monospace; font-size: 12px;">${tree.latitude.toFixed(4)}, ${tree.longitude.toFixed(4)}</span>
559
+ ${tree.height ? `<strong style="color: #374151;">Height:</strong><span>${tree.height}m</span>` : ''}
560
+ ${tree.width ? `<strong style="color: #374151;">Girth:</strong><span>${tree.width}cm</span>` : ''}
561
+ <strong style="color: #374151;">Added by:</strong><span>${tree.created_by || 'Unknown'}</span>
562
+ <strong style="color: #374151;">Date:</strong><span>${new Date(tree.created_at).toLocaleDateString()}</span>
563
+ </div>
 
 
 
 
 
 
 
 
564
  </div>
565
+
566
+ ${tree.notes ? `
567
+ <div style="margin-bottom: 16px; padding: 12px; background: #f8fafc; border-radius: 6px; border-left: 4px solid #e2e8f0;">
568
+ <strong style="color: #374151; font-size: 13px; display: block; margin-bottom: 6px;">Notes:</strong>
569
+ <div style="color: #6b7280; font-size: 12px; line-height: 1.4; word-wrap: break-word;">
570
+ ${tree.notes}
571
+ </div>
572
+ </div>
573
+ ` : ''}
574
+
575
+ ${canEdit || canDelete ? `
576
+ <div style="display: flex; gap: 8px; margin-top: 16px; padding-top: 16px; border-top: 1px solid #e5e7eb;">
577
+ ${canEdit ? `
578
+ <button onclick="mapApp.editTree(${tree.id})"
579
+ style="flex: 1; background: #3b82f6; color: white; border: none; padding: 8px 12px; border-radius: 6px; cursor: pointer; font-size: 12px; font-weight: 600; transition: all 0.2s; min-height: 36px;"
580
+ onmouseover="this.style.backgroundColor='#2563eb'; this.style.transform='translateY(-1px)';"
581
+ onmouseout="this.style.backgroundColor='#3b82f6'; this.style.transform='translateY(0)';">
582
+ ✏️ Edit
583
+ </button>
584
+ ` : ''}
585
+ ${canDelete ? `
586
+ <button onclick="mapApp.deleteTree(${tree.id})"
587
+ style="flex: 1; background: #ef4444; color: white; border: none; padding: 8px 12px; border-radius: 6px; cursor: pointer; font-size: 12px; font-weight: 600; transition: all 0.2s; min-height: 36px;"
588
+ onmouseover="this.style.backgroundColor='#dc2626'; this.style.transform='translateY(-1px)';"
589
+ onmouseout="this.style.backgroundColor='#ef4444'; this.style.transform='translateY(0)';">
590
+ 🗑️ Delete
591
+ </button>
592
+ ` : ''}
593
+ </div>
594
+ ` : ''}
595
+ </div>
596
  </div>
597
  `;
598
 
599
  marker.bindPopup(popupContent, {
600
+ maxWidth: 320,
601
+ minWidth: 280,
602
+ className: 'tree-popup',
603
+ closeButton: true,
604
+ autoClose: true,
605
+ autoPan: true,
606
+ closeOnEscapeKey: true
607
  });
608
 
609
  this.treeMarkers.push(marker);