RoyAalekh commited on
Commit
c6755c8
Β·
1 Parent(s): d69ea55

🎨 Complete UI overhaul with modern professional interface

Browse files

✨ Features:
- Modern, responsive form and map pages with professional design
- Fixed JavaScript errors in login page (fillCredentials function)
- Updated app.js to work with new HTML structure
- Added environment configuration with secure password handling

πŸ› Fixes:
- Resolved TypeError in login page JavaScript
- Fixed message display CSS classes
- Added null checks for missing UI elements
- Proper event handling in login interface

πŸ”§ Technical:
- Dual authentication system (cookies + headers)
- Mobile-responsive design with smooth transitions
- Professional map interface replacing childish UI
- Environment-based authentication security

Ready for production deployment on HuggingFace Spaces!

Files changed (6) hide show
  1. static/app.js +54 -41
  2. static/index.html +643 -432
  3. static/index_old.html +671 -0
  4. static/login.html +19 -6
  5. static/map.html +562 -307
  6. static/map_old.html +545 -0
static/app.js CHANGED
@@ -71,45 +71,38 @@ class TreeTrackApp {
71
  }
72
 
73
  setupUserInterface() {
74
- // Add user info to header
75
  this.displayUserInfo();
76
 
77
  // Add logout functionality
78
  this.addLogoutButton();
79
-
80
- // Add custom styles for new elements
81
- this.addCustomStyles();
82
  }
83
 
84
  displayUserInfo() {
85
  if (!this.currentUser) return;
86
 
87
- const headerContent = document.querySelector('.header-content');
88
- if (headerContent) {
89
- // Create user info display
90
- const userInfo = document.createElement('div');
91
- userInfo.className = 'user-info';
92
- userInfo.innerHTML = `
93
- <div class="user-greeting">Welcome, ${this.currentUser.full_name}</div>
94
- <div class="user-role">${this.currentUser.role}</div>
95
- `;
96
-
97
- // Insert before the map link
98
- const mapLink = headerContent.querySelector('.map-link');
99
- if (mapLink) {
100
- headerContent.insertBefore(userInfo, mapLink);
101
- }
102
  }
103
  }
104
 
105
  addLogoutButton() {
106
- const headerContent = document.querySelector('.header-content');
107
- if (headerContent) {
108
- const logoutBtn = document.createElement('button');
109
- logoutBtn.className = 'btn btn-outline btn-small logout-btn';
110
- logoutBtn.innerHTML = 'πŸšͺ Logout';
111
  logoutBtn.addEventListener('click', () => this.logout());
112
- headerContent.appendChild(logoutBtn);
113
  }
114
  }
115
 
@@ -326,8 +319,11 @@ class TreeTrackApp {
326
  // GPS location
327
  document.getElementById('getLocation').addEventListener('click', () => this.getCurrentLocation());
328
 
329
- // Audio recording
330
- document.getElementById('recordBtn').addEventListener('click', () => this.toggleRecording());
 
 
 
331
 
332
  // Audio file upload
333
  document.getElementById('audioUpload').addEventListener('click', () => this.selectAudioFile());
@@ -528,12 +524,16 @@ class TreeTrackApp {
528
  this.mediaRecorder.start();
529
  this.isRecording = true;
530
 
531
- // Update UI
532
  const recordBtn = document.getElementById('recordBtn');
533
  const status = document.getElementById('recordingStatus');
534
- recordBtn.classList.add('recording');
535
- recordBtn.innerHTML = '⏹';
536
- status.textContent = 'Recording... Click to stop';
 
 
 
 
537
 
538
  } catch (error) {
539
  console.error('Error starting recording:', error);
@@ -547,12 +547,16 @@ class TreeTrackApp {
547
  this.mediaRecorder.stream.getTracks().forEach(track => track.stop());
548
  this.isRecording = false;
549
 
550
- // Update UI
551
  const recordBtn = document.getElementById('recordBtn');
552
  const status = document.getElementById('recordingStatus');
553
- recordBtn.classList.remove('recording');
554
- recordBtn.innerHTML = '●';
555
- status.textContent = 'Recording saved!';
 
 
 
 
556
  }
557
  }
558
 
@@ -650,13 +654,22 @@ class TreeTrackApp {
650
  el.innerHTML = '';
651
  });
652
 
653
- // Reset audio controls
654
  const audioElement = document.getElementById('audioPlayback');
655
- audioElement.classList.add('hidden');
656
- audioElement.src = '';
 
 
 
 
 
 
 
657
 
658
- document.getElementById('recordingStatus').textContent = 'Click to start recording';
659
- document.getElementById('audioUploadResult').innerHTML = '';
 
 
660
  }
661
 
662
  async loadTrees() {
@@ -876,7 +889,7 @@ class TreeTrackApp {
876
 
877
  showMessage(message, type) {
878
  const messageDiv = document.getElementById('message');
879
- messageDiv.className = type === 'error' ? 'error-message' : 'success-message';
880
  messageDiv.textContent = message;
881
 
882
  // Auto-hide after 5 seconds
 
71
  }
72
 
73
  setupUserInterface() {
74
+ // Update existing user info elements
75
  this.displayUserInfo();
76
 
77
  // Add logout functionality
78
  this.addLogoutButton();
 
 
 
79
  }
80
 
81
  displayUserInfo() {
82
  if (!this.currentUser) return;
83
 
84
+ // Update existing user info elements in the new HTML structure
85
+ const userNameEl = document.getElementById('userName');
86
+ const userRoleEl = document.getElementById('userRole');
87
+ const userAvatarEl = document.getElementById('userAvatar');
88
+
89
+ if (userNameEl) {
90
+ userNameEl.textContent = this.currentUser.full_name;
91
+ }
92
+
93
+ if (userRoleEl) {
94
+ userRoleEl.textContent = this.currentUser.role;
95
+ }
96
+
97
+ if (userAvatarEl) {
98
+ userAvatarEl.textContent = this.currentUser.full_name.charAt(0).toUpperCase();
99
  }
100
  }
101
 
102
  addLogoutButton() {
103
+ const logoutBtn = document.getElementById('logoutBtn');
104
+ if (logoutBtn) {
 
 
 
105
  logoutBtn.addEventListener('click', () => this.logout());
 
106
  }
107
  }
108
 
 
319
  // GPS location
320
  document.getElementById('getLocation').addEventListener('click', () => this.getCurrentLocation());
321
 
322
+ // Audio recording - check if element exists
323
+ const recordBtn = document.getElementById('recordBtn');
324
+ if (recordBtn) {
325
+ recordBtn.addEventListener('click', () => this.toggleRecording());
326
+ }
327
 
328
  // Audio file upload
329
  document.getElementById('audioUpload').addEventListener('click', () => this.selectAudioFile());
 
524
  this.mediaRecorder.start();
525
  this.isRecording = true;
526
 
527
+ // Update UI - check if elements exist
528
  const recordBtn = document.getElementById('recordBtn');
529
  const status = document.getElementById('recordingStatus');
530
+ if (recordBtn) {
531
+ recordBtn.classList.add('recording');
532
+ recordBtn.innerHTML = '⏹';
533
+ }
534
+ if (status) {
535
+ status.textContent = 'Recording... Click to stop';
536
+ }
537
 
538
  } catch (error) {
539
  console.error('Error starting recording:', error);
 
547
  this.mediaRecorder.stream.getTracks().forEach(track => track.stop());
548
  this.isRecording = false;
549
 
550
+ // Update UI - check if elements exist
551
  const recordBtn = document.getElementById('recordBtn');
552
  const status = document.getElementById('recordingStatus');
553
+ if (recordBtn) {
554
+ recordBtn.classList.remove('recording');
555
+ recordBtn.innerHTML = '●';
556
+ }
557
+ if (status) {
558
+ status.textContent = 'Recording saved!';
559
+ }
560
  }
561
  }
562
 
 
654
  el.innerHTML = '';
655
  });
656
 
657
+ // Reset audio controls - check if elements exist
658
  const audioElement = document.getElementById('audioPlayback');
659
+ if (audioElement) {
660
+ audioElement.classList.add('hidden');
661
+ audioElement.src = '';
662
+ }
663
+
664
+ const recordingStatus = document.getElementById('recordingStatus');
665
+ if (recordingStatus) {
666
+ recordingStatus.textContent = 'Click to start recording';
667
+ }
668
 
669
+ const audioUploadResult = document.getElementById('audioUploadResult');
670
+ if (audioUploadResult) {
671
+ audioUploadResult.innerHTML = '';
672
+ }
673
  }
674
 
675
  async loadTrees() {
 
889
 
890
  showMessage(message, type) {
891
  const messageDiv = document.getElementById('message');
892
+ messageDiv.className = `message ${type === 'error' ? 'error' : 'success'}`;
893
  messageDiv.textContent = message;
894
 
895
  // Auto-hide after 5 seconds
static/index.html CHANGED
@@ -6,9 +6,62 @@
6
  <meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate">
7
  <meta http-equiv="Pragma" content="no-cache">
8
  <meta http-equiv="Expires" content="0">
9
- <title>TreeTrack - Field Research Tool</title>
10
  <style>
11
- @import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap');
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
12
 
13
  * {
14
  margin: 0;
@@ -18,34 +71,46 @@
18
 
19
  body {
20
  font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
 
21
  line-height: 1.6;
22
- color: #1e293b;
23
- background: #f8fafc;
24
  min-height: 100vh;
 
 
25
  }
26
 
 
27
  .header {
28
- background: linear-gradient(135deg, #1e40af 0%, #3b82f6 100%);
29
  color: white;
30
- padding: 1.5rem 0;
31
- margin-bottom: 2rem;
32
- box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1);
 
 
33
  }
34
 
35
  .header-content {
36
- max-width: 1200px;
37
  margin: 0 auto;
38
- padding: 0 1rem;
39
  display: flex;
40
  justify-content: space-between;
41
  align-items: center;
42
  flex-wrap: wrap;
43
- gap: 1rem;
 
 
 
 
 
 
44
  }
45
 
46
  .header h1 {
47
- font-size: 1.875rem;
48
- font-weight: 600;
49
  margin: 0;
50
  letter-spacing: -0.025em;
51
  }
@@ -53,456 +118,544 @@
53
  .header-subtitle {
54
  font-size: 0.875rem;
55
  opacity: 0.9;
56
- margin-top: 0.25rem;
57
  font-weight: 400;
58
  }
59
 
60
- .map-link {
 
 
 
 
 
 
 
 
 
 
61
  background: rgba(255, 255, 255, 0.1);
62
- color: white;
63
- padding: 0.5rem 1rem;
64
- border-radius: 6px;
65
- text-decoration: none;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
66
  font-weight: 500;
67
- transition: background-color 0.2s;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
68
  border: 1px solid rgba(255, 255, 255, 0.2);
69
  }
70
 
71
- .map-link:hover {
72
  background: rgba(255, 255, 255, 0.2);
 
 
 
 
 
 
 
73
  }
74
 
75
- .container {
76
- max-width: 1200px;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
77
  margin: 0 auto;
78
- padding: 0 1rem 2rem;
 
 
 
79
  display: grid;
80
  grid-template-columns: 1fr;
81
- gap: 2rem;
82
  }
83
 
84
- @media (min-width: 1024px) {
85
- .container {
86
  grid-template-columns: 2fr 1fr;
87
  }
88
  }
89
 
90
- .form-container {
 
91
  background: white;
92
- border-radius: 12px;
93
- padding: 2rem;
94
- box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px 0 rgba(0, 0, 0, 0.06);
95
- border: 1px solid #e2e8f0;
 
96
  }
97
 
98
- .sidebar-container {
99
- background: white;
100
- border-radius: 12px;
101
- padding: 1.5rem;
102
- box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px 0 rgba(0, 0, 0, 0.06);
103
- border: 1px solid #e2e8f0;
104
- height: fit-content;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
105
  }
106
 
 
 
 
 
 
107
  .form-section {
108
- margin-bottom: 2rem;
109
- padding: 1.5rem;
110
- border: 1px solid #e2e8f0;
111
- border-radius: 8px;
112
- background: #fafbfc;
 
 
 
 
113
  }
114
 
115
  .section-title {
116
  font-size: 1.125rem;
117
  font-weight: 600;
118
- color: #374151;
119
- margin-bottom: 1rem;
120
- padding-bottom: 0.5rem;
121
- border-bottom: 1px solid #e2e8f0;
 
 
 
 
122
  }
123
 
 
124
  .form-group {
125
- margin-bottom: 1.5rem;
126
  }
127
 
128
  .form-row {
129
  display: grid;
130
  grid-template-columns: 1fr;
131
- gap: 1rem;
132
  }
133
 
134
- @media (min-width: 640px) {
135
  .form-row {
136
  grid-template-columns: 1fr 1fr;
137
  }
138
  }
139
 
140
- label {
141
  display: block;
142
- margin-bottom: 0.5rem;
143
- font-weight: 500;
144
- color: #374151;
145
  font-size: 0.875rem;
 
 
 
 
 
 
 
 
 
146
  }
147
 
148
- input, textarea, select {
149
  width: 100%;
150
- padding: 0.75rem;
151
- border: 1px solid #d1d5db;
152
- border-radius: 6px;
153
  font-size: 0.875rem;
154
- transition: all 0.2s;
 
155
  background: white;
 
 
 
 
156
  }
157
 
158
- input:focus, textarea:focus, select:focus {
159
- outline: none;
160
- border-color: #3b82f6;
161
- box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1);
162
  }
163
 
164
- textarea {
 
 
 
 
 
 
165
  resize: vertical;
166
  min-height: 100px;
167
  }
168
 
 
 
 
 
 
 
 
 
 
 
169
  .multi-select {
170
- border: 1px solid #d1d5db;
171
- border-radius: 6px;
172
- padding: 0.75rem;
173
- max-height: 120px;
174
- overflow-y: auto;
175
  background: white;
 
 
176
  }
177
 
178
- .multi-select label {
179
  display: flex;
180
  align-items: center;
181
- margin-bottom: 0.5rem;
182
- font-weight: normal;
 
183
  cursor: pointer;
184
- padding: 0.25rem;
185
- border-radius: 4px;
186
- transition: background-color 0.2s;
187
  }
188
 
189
- .multi-select label:hover {
190
- background-color: #f3f4f6;
191
  }
192
 
193
- .multi-select input[type="checkbox"] {
194
- width: auto;
195
- margin-right: 0.5rem;
196
  }
197
 
198
- .file-upload {
199
- border: 2px dashed #d1d5db;
200
- border-radius: 6px;
201
- padding: 1.5rem;
202
- text-align: center;
203
- cursor: pointer;
204
- transition: all 0.2s;
205
- background: #f9fafb;
206
- margin-top: 0.5rem;
207
  }
208
 
209
- .file-upload:hover {
210
- border-color: #3b82f6;
211
- background: #f0f9ff;
 
 
212
  }
213
 
214
- .photo-category {
215
- display: grid;
216
- grid-template-columns: 1fr auto;
217
- gap: 0.75rem;
218
- align-items: center;
219
- margin-bottom: 1rem;
220
- padding: 1rem;
221
- border: 1px solid #e2e8f0;
222
- border-radius: 6px;
223
- background: white;
224
  }
225
 
226
- .btn {
227
- padding: 0.75rem 1.5rem;
228
- border: none;
229
- border-radius: 6px;
230
- cursor: pointer;
231
- font-size: 0.875rem;
232
- font-weight: 500;
233
- transition: all 0.2s;
234
- text-decoration: none;
235
- display: inline-flex;
236
- align-items: center;
237
- justify-content: center;
238
- gap: 0.5rem;
239
  }
240
 
241
- .btn-primary {
242
- background: #3b82f6;
243
- color: white;
244
  }
245
 
246
- .btn-primary:hover {
247
- background: #2563eb;
 
248
  }
249
 
250
- .btn-secondary {
251
- background: #6b7280;
252
- color: white;
253
  }
254
 
255
- .btn-secondary:hover {
256
- background: #4b5563;
 
 
257
  }
258
 
259
- .btn-outline {
260
- background: transparent;
261
- color: #3b82f6;
262
- border: 1px solid #3b82f6;
 
263
  }
264
 
265
- .btn-outline:hover {
266
- background: #3b82f6;
267
- color: white;
268
  }
269
 
270
- .btn-small {
271
- padding: 0.5rem 1rem;
272
- font-size: 0.75rem;
273
  }
274
 
275
- .current-location {
 
276
  display: flex;
277
- align-items: center;
278
- gap: 0.5rem;
 
 
 
279
  }
280
 
281
- .success-message, .error-message {
282
- padding: 0.75rem 1rem;
283
- border-radius: 6px;
284
- margin: 1rem 0;
285
- font-weight: 500;
 
 
 
 
 
 
 
 
 
 
 
286
  font-size: 0.875rem;
 
 
287
  }
288
 
289
- .success-message {
290
- background: #dcfce7;
291
- color: #166534;
292
- border: 1px solid #bbf7d0;
293
  }
294
 
295
- .error-message {
296
- background: #fee2e2;
297
- color: #991b1b;
298
- border: 1px solid #fecaca;
299
  }
300
 
 
301
  .sidebar-title {
302
  font-size: 1.125rem;
303
  font-weight: 600;
304
- color: #374151;
305
- margin-bottom: 1rem;
306
- padding-bottom: 0.5rem;
307
- border-bottom: 1px solid #e2e8f0;
308
  }
309
 
310
  .tree-list {
311
- max-height: 60vh;
312
  overflow-y: auto;
313
  }
314
 
315
  .tree-item {
316
- padding: 1rem;
317
- border: 1px solid #e2e8f0;
318
- border-radius: 6px;
319
- margin-bottom: 0.75rem;
320
  background: white;
321
- transition: all 0.2s;
322
  }
323
 
324
  .tree-item:hover {
325
- box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1);
326
- border-color: #3b82f6;
 
 
 
 
 
 
 
 
327
  }
328
 
329
  .tree-id {
330
  font-weight: 600;
331
- color: #1e40af;
332
  font-size: 0.875rem;
333
  }
334
 
335
- .tree-info {
336
- color: #6b7280;
337
- font-size: 0.75rem;
338
- margin-top: 0.25rem;
339
- line-height: 1.4;
340
- }
341
-
342
- .loading {
343
- text-align: center;
344
- padding: 2rem;
345
- color: #6b7280;
346
- }
347
-
348
- .audio-controls {
349
  display: flex;
350
- gap: 0.75rem;
351
- align-items: center;
352
- margin-top: 0.75rem;
353
  }
354
 
355
- .record-btn {
356
- background: #ef4444;
357
- color: white;
358
  border: none;
359
- border-radius: 50%;
360
- width: 3rem;
361
- height: 3rem;
362
- font-size: 1.125rem;
363
  cursor: pointer;
364
- transition: all 0.2s;
365
- }
366
-
367
- .record-btn:hover {
368
- background: #dc2626;
369
- }
370
-
371
- .record-btn.recording {
372
- background: #10b981;
373
- animation: pulse 1.5s infinite;
374
- }
375
-
376
- @keyframes pulse {
377
- 0% { transform: scale(1); }
378
- 50% { transform: scale(1.05); }
379
- 100% { transform: scale(1); }
380
- }
381
-
382
- .hidden {
383
- display: none;
384
- }
385
-
386
- .form-actions {
387
  display: flex;
388
- gap: 1rem;
389
- justify-content: flex-end;
390
- margin-top: 2rem;
391
- padding-top: 1.5rem;
392
- border-top: 1px solid #e2e8f0;
393
  }
394
 
395
- @media (max-width: 640px) {
396
- .form-actions {
397
- flex-direction: column;
398
- }
399
-
400
- .btn {
401
- width: 100%;
402
- }
403
  }
404
 
405
- .uploaded-file {
406
- margin-top: 0.5rem;
407
- padding: 0.5rem 0.75rem;
408
- background: #f0fdf4;
409
- border: 1px solid #bbf7d0;
410
- border-radius: 4px;
411
  font-size: 0.75rem;
412
- color: #166534;
413
  }
414
 
415
- .form-description {
416
- color: #6b7280;
 
 
 
 
 
417
  font-size: 0.875rem;
418
- margin-bottom: 2rem;
419
- line-height: 1.5;
420
- }
421
-
422
- /* Auto-suggestion styles */
423
- .autocomplete-container {
424
- position: relative;
425
- display: inline-block;
426
- width: 100%;
427
  }
428
 
429
- .autocomplete-dropdown {
430
- position: absolute;
431
- top: 100%;
432
- left: 0;
433
- right: 0;
434
- background: white;
435
- border: 1px solid #d1d5db;
436
- border-top: none;
437
- border-radius: 0 0 6px 6px;
438
- box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1);
439
- max-height: 200px;
440
- overflow-y: auto;
441
- z-index: 1000;
442
- display: none;
443
- }
444
-
445
- .autocomplete-item {
446
- padding: 0.75rem;
447
- cursor: pointer;
448
- border-bottom: 1px solid #f3f4f6;
449
- transition: background-color 0.2s;
450
- }
451
-
452
- .autocomplete-item:last-child {
453
- border-bottom: none;
454
- }
455
-
456
- .autocomplete-item:hover,
457
- .autocomplete-item.highlighted {
458
- background-color: #f3f4f6;
459
- }
460
-
461
- .autocomplete-primary {
462
- font-weight: 500;
463
- color: #1e293b;
464
- font-size: 0.875rem;
465
  }
466
 
467
- .autocomplete-secondary {
468
- font-size: 0.75rem;
469
- color: #6b7280;
470
- margin-top: 0.25rem;
471
- line-height: 1.3;
472
  }
473
 
474
- .autocomplete-badge {
475
- display: inline-block;
476
- background: #e0f2fe;
477
- color: #0369a1;
478
- font-size: 0.625rem;
479
- font-weight: 500;
480
- padding: 0.125rem 0.375rem;
481
- border-radius: 4px;
482
- margin-top: 0.25rem;
483
- margin-right: 0.25rem;
484
  }
485
 
486
- .autocomplete-loading {
487
- padding: 0.75rem;
488
- text-align: center;
489
- color: #6b7280;
490
- font-size: 0.75rem;
491
- font-style: italic;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
492
  }
493
-
494
- .autocomplete-no-results {
495
- padding: 0.75rem;
496
- text-align: center;
497
- color: #9ca3af;
498
- font-size: 0.75rem;
499
- font-style: italic;
500
  }
501
  </style>
502
  <script>
503
  // Force refresh if we detect cached version
504
  (function() {
505
- const currentVersion = '3.1754653728';
506
  const lastVersion = sessionStorage.getItem('treetrack_version');
507
  if (!lastVersion || lastVersion !== currentVersion) {
508
  sessionStorage.setItem('treetrack_version', currentVersion);
@@ -517,155 +670,213 @@
517
  <body>
518
  <div class="header">
519
  <div class="header-content">
520
- <div>
521
- <h1>TreeTrack</h1>
522
- <div class="header-subtitle">Professional Field Research & Documentation</div>
 
 
 
 
 
 
 
 
 
 
 
523
  </div>
524
- <a href="/static/map.html" class="map-link">View Map</a>
525
  </div>
526
  </div>
527
 
528
- <div class="container">
529
- <div class="form-container">
530
- <div class="form-description">
531
- Complete the form below to document tree specimens in the field. All fields marked with * are required.
532
- </div>
533
-
534
- <form id="treeForm">
535
- <!-- Section 1: Location -->
536
- <div class="form-section">
537
- <h3 class="section-title">Geographic Location</h3>
538
- <div class="form-row">
539
- <div class="form-group">
540
- <label for="latitude">Latitude *</label>
541
- <div class="current-location">
542
- <input type="number" id="latitude" step="0.0000001" min="-90" max="90" required>
543
- <button type="button" id="getLocation" class="btn btn-outline btn-small">Get GPS</button>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
544
  </div>
545
  </div>
546
- <div class="form-group">
547
- <label for="longitude">Longitude *</label>
548
- <input type="number" id="longitude" step="0.0000001" min="-180" max="180" required>
549
- </div>
550
- </div>
551
- <div class="form-group">
552
- <a href="/static/map.html" class="btn btn-outline" style="width: 100%; text-align: center;">Select from Interactive Map</a>
553
- </div>
554
- </div>
555
 
556
- <!-- Section 2: Identification -->
557
- <div class="form-section">
558
- <h3 class="section-title">Tree Identification</h3>
559
- <div class="form-group">
560
- <label for="localName">Local Name (Assamese)</label>
561
- <input type="text" id="localName" placeholder="Enter local Assamese name">
562
- </div>
563
- <div class="form-group">
564
- <label for="scientificName">Scientific Name</label>
565
- <input type="text" id="scientificName" placeholder="e.g., Ficus benghalensis">
566
- </div>
567
- <div class="form-group">
568
- <label for="commonName">Common Name</label>
569
- <input type="text" id="commonName" placeholder="e.g., Banyan Tree">
570
- </div>
571
- <div class="form-group">
572
- <label for="treeCode">Tree Reference Code</label>
573
- <input type="text" id="treeCode" placeholder="e.g., C.A, A-G1" maxlength="20">
574
- </div>
575
- </div>
 
 
 
 
 
 
 
576
 
577
- <!-- Section 3: Measurements -->
578
- <div class="form-section">
579
- <h3 class="section-title">Physical Measurements</h3>
580
- <div class="form-row">
581
- <div class="form-group">
582
- <label for="height">Height (meters)</label>
583
- <input type="number" id="height" step="0.1" min="0" max="200" placeholder="15.5">
 
 
 
 
 
 
 
 
 
 
584
  </div>
585
- <div class="form-group">
586
- <label for="width">Girth/DBH (cm)</label>
587
- <input type="number" id="width" step="0.1" min="0" max="2000" placeholder="45.2">
 
 
 
 
 
 
 
 
 
 
588
  </div>
589
- </div>
590
- </div>
591
 
592
- <!-- Section 4: Utility -->
593
- <div class="form-section">
594
- <h3 class="section-title">Ecological & Cultural Utility</h3>
595
- <div class="form-group">
596
- <label>Select applicable utilities:</label>
597
- <div id="utilityOptions" class="multi-select">
598
- <!-- Options loaded dynamically -->
 
 
 
 
 
599
  </div>
600
- </div>
601
- </div>
602
 
603
- <!-- Section 5: Phenology -->
604
- <div class="form-section">
605
- <h3 class="section-title">Phenology Assessment</h3>
606
- <div class="form-group">
607
- <label>Current development stages:</label>
608
- <div id="phenologyOptions" class="multi-select">
609
- <!-- Options loaded dynamically -->
 
 
 
610
  </div>
611
- </div>
612
- </div>
613
 
614
- <!-- Section 6: Photography -->
615
- <div class="form-section">
616
- <h3 class="section-title">Photographic Documentation</h3>
617
- <div id="photoCategories">
618
- <!-- Photo categories loaded dynamically -->
619
- </div>
620
- </div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
621
 
622
- <!-- Section 7: Storytelling -->
623
- <div class="form-section">
624
- <h3 class="section-title">Cultural Documentation</h3>
625
- <div class="form-group">
626
- <label for="storytellingText">Stories, Histories & Narratives</label>
627
- <textarea id="storytellingText" placeholder="Share any stories, historical context, or cultural significance..." maxlength="5000"></textarea>
628
- </div>
629
- <div class="form-group">
630
- <label>Audio Recording</label>
631
- <div class="audio-controls">
632
- <button type="button" id="recordBtn" class="record-btn" title="Record Audio">●</button>
633
- <span id="recordingStatus">Click to start recording</span>
634
- <audio id="audioPlayback" controls class="hidden"></audio>
635
  </div>
636
- <div class="file-upload" id="audioUpload">
637
- Click to upload audio file or drag and drop
 
 
638
  </div>
639
- <div id="audioUploadResult"></div>
640
- </div>
641
- </div>
642
 
643
- <!-- Section 8: Notes -->
644
- <div class="form-section">
645
- <h3 class="section-title">Field Notes</h3>
646
- <div class="form-group">
647
- <label for="notes">Additional Observations</label>
648
- <textarea id="notes" placeholder="Any additional observations, notes, or remarks..." maxlength="2000"></textarea>
649
- </div>
650
  </div>
 
651
 
652
- <div class="form-actions">
653
- <button type="button" id="resetForm" class="btn btn-secondary">Reset Form</button>
654
- <button type="submit" class="btn btn-primary">Save Tree Record</button>
 
 
 
 
 
 
 
 
 
655
  </div>
656
- </form>
657
-
658
- <div id="message"></div>
659
- </div>
660
-
661
- <div class="sidebar-container">
662
- <h3 class="sidebar-title">Recent Trees</h3>
663
- <div id="treeList" class="tree-list">
664
- <div class="loading">Loading trees...</div>
665
  </div>
666
  </div>
667
  </div>
668
 
669
- <script src="/static/app.js?v=3.1754653728&t=1754653728"></script>
670
  </body>
671
  </html>
 
6
  <meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate">
7
  <meta http-equiv="Pragma" content="no-cache">
8
  <meta http-equiv="Expires" content="0">
9
+ <title>TreeTrack - Professional Field Research</title>
10
  <style>
11
+ @import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700;800&display=swap');
12
+
13
+ :root {
14
+ /* Colors */
15
+ --primary-50: #eff6ff;
16
+ --primary-100: #dbeafe;
17
+ --primary-500: #3b82f6;
18
+ --primary-600: #2563eb;
19
+ --primary-700: #1d4ed8;
20
+ --primary-900: #1e3a8a;
21
+
22
+ --gray-50: #f8fafc;
23
+ --gray-100: #f1f5f9;
24
+ --gray-200: #e2e8f0;
25
+ --gray-300: #cbd5e1;
26
+ --gray-400: #94a3b8;
27
+ --gray-500: #64748b;
28
+ --gray-600: #475569;
29
+ --gray-700: #334155;
30
+ --gray-800: #1e293b;
31
+ --gray-900: #0f172a;
32
+
33
+ --green-50: #f0fdf4;
34
+ --green-500: #22c55e;
35
+ --green-600: #16a34a;
36
+
37
+ --red-50: #fef2f2;
38
+ --red-500: #ef4444;
39
+ --red-600: #dc2626;
40
+
41
+ /* Spacing */
42
+ --space-1: 0.25rem;
43
+ --space-2: 0.5rem;
44
+ --space-3: 0.75rem;
45
+ --space-4: 1rem;
46
+ --space-5: 1.25rem;
47
+ --space-6: 1.5rem;
48
+ --space-8: 2rem;
49
+ --space-12: 3rem;
50
+ --space-16: 4rem;
51
+
52
+ /* Radius */
53
+ --radius-sm: 0.375rem;
54
+ --radius-md: 0.5rem;
55
+ --radius-lg: 0.75rem;
56
+ --radius-xl: 1rem;
57
+ --radius-2xl: 1.5rem;
58
+
59
+ /* Shadows */
60
+ --shadow-sm: 0 1px 2px 0 rgb(0 0 0 / 0.05);
61
+ --shadow-md: 0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1);
62
+ --shadow-lg: 0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1);
63
+ --shadow-xl: 0 20px 25px -5px rgb(0 0 0 / 0.1), 0 8px 10px -6px rgb(0 0 0 / 0.1);
64
+ }
65
 
66
  * {
67
  margin: 0;
 
71
 
72
  body {
73
  font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
74
+ font-feature-settings: 'cv11' 1, 'ss01' 1;
75
  line-height: 1.6;
76
+ color: var(--gray-800);
77
+ background: var(--gray-50);
78
  min-height: 100vh;
79
+ -webkit-font-smoothing: antialiased;
80
+ -moz-osx-font-smoothing: grayscale;
81
  }
82
 
83
+ /* Header */
84
  .header {
85
+ background: linear-gradient(135deg, var(--primary-600) 0%, var(--primary-700) 100%);
86
  color: white;
87
+ position: sticky;
88
+ top: 0;
89
+ z-index: 100;
90
+ backdrop-filter: blur(12px);
91
+ border-bottom: 1px solid rgba(255, 255, 255, 0.1);
92
  }
93
 
94
  .header-content {
95
+ max-width: 1400px;
96
  margin: 0 auto;
97
+ padding: var(--space-4) var(--space-6);
98
  display: flex;
99
  justify-content: space-between;
100
  align-items: center;
101
  flex-wrap: wrap;
102
+ gap: var(--space-4);
103
+ }
104
+
105
+ .header-brand {
106
+ display: flex;
107
+ flex-direction: column;
108
+ gap: var(--space-1);
109
  }
110
 
111
  .header h1 {
112
+ font-size: 1.75rem;
113
+ font-weight: 700;
114
  margin: 0;
115
  letter-spacing: -0.025em;
116
  }
 
118
  .header-subtitle {
119
  font-size: 0.875rem;
120
  opacity: 0.9;
 
121
  font-weight: 400;
122
  }
123
 
124
+ .header-actions {
125
+ display: flex;
126
+ align-items: center;
127
+ gap: var(--space-3);
128
+ }
129
+
130
+ .user-info {
131
+ display: flex;
132
+ align-items: center;
133
+ gap: var(--space-3);
134
+ padding: var(--space-2) var(--space-4);
135
  background: rgba(255, 255, 255, 0.1);
136
+ border-radius: var(--radius-xl);
137
+ border: 1px solid rgba(255, 255, 255, 0.2);
138
+ backdrop-filter: blur(8px);
139
+ }
140
+
141
+ .user-avatar {
142
+ width: 32px;
143
+ height: 32px;
144
+ border-radius: 50%;
145
+ background: var(--primary-500);
146
+ display: flex;
147
+ align-items: center;
148
+ justify-content: center;
149
+ font-weight: 600;
150
+ font-size: 0.875rem;
151
+ }
152
+
153
+ .user-details {
154
+ display: flex;
155
+ flex-direction: column;
156
+ gap: 0;
157
+ }
158
+
159
+ .user-name {
160
+ font-size: 0.875rem;
161
+ font-weight: 600;
162
+ }
163
+
164
+ .user-role {
165
+ font-size: 0.75rem;
166
+ opacity: 0.8;
167
+ text-transform: capitalize;
168
+ }
169
+
170
+ .btn {
171
+ display: inline-flex;
172
+ align-items: center;
173
+ justify-content: center;
174
+ gap: var(--space-2);
175
+ padding: var(--space-2) var(--space-4);
176
+ font-size: 0.875rem;
177
  font-weight: 500;
178
+ line-height: 1.25;
179
+ border-radius: var(--radius-md);
180
+ border: none;
181
+ cursor: pointer;
182
+ transition: all 0.15s ease;
183
+ text-decoration: none;
184
+ outline: none;
185
+ position: relative;
186
+ overflow: hidden;
187
+ }
188
+
189
+ .btn-primary {
190
+ background: var(--primary-500);
191
+ color: white;
192
+ }
193
+
194
+ .btn-primary:hover {
195
+ background: var(--primary-600);
196
+ transform: translateY(-1px);
197
+ box-shadow: var(--shadow-md);
198
+ }
199
+
200
+ .btn-secondary {
201
+ background: rgba(255, 255, 255, 0.1);
202
+ color: white;
203
  border: 1px solid rgba(255, 255, 255, 0.2);
204
  }
205
 
206
+ .btn-secondary:hover {
207
  background: rgba(255, 255, 255, 0.2);
208
+ transform: translateY(-1px);
209
+ }
210
+
211
+ .btn-outline {
212
+ background: transparent;
213
+ color: var(--gray-700);
214
+ border: 1px solid var(--gray-300);
215
  }
216
 
217
+ .btn-outline:hover {
218
+ background: var(--gray-50);
219
+ border-color: var(--gray-400);
220
+ }
221
+
222
+ .btn-lg {
223
+ padding: var(--space-3) var(--space-6);
224
+ font-size: 1rem;
225
+ }
226
+
227
+ .btn-sm {
228
+ padding: var(--space-1) var(--space-3);
229
+ font-size: 0.8125rem;
230
+ }
231
+
232
+ /* Main Content */
233
+ .main-container {
234
+ max-width: 1400px;
235
  margin: 0 auto;
236
+ padding: var(--space-8) var(--space-6);
237
+ }
238
+
239
+ .content-grid {
240
  display: grid;
241
  grid-template-columns: 1fr;
242
+ gap: var(--space-8);
243
  }
244
 
245
+ @media (min-width: 1200px) {
246
+ .content-grid {
247
  grid-template-columns: 2fr 1fr;
248
  }
249
  }
250
 
251
+ /* Cards */
252
+ .card {
253
  background: white;
254
+ border-radius: var(--radius-xl);
255
+ border: 1px solid var(--gray-200);
256
+ box-shadow: var(--shadow-sm);
257
+ transition: all 0.2s ease;
258
+ overflow: hidden;
259
  }
260
 
261
+ .card:hover {
262
+ box-shadow: var(--shadow-md);
263
+ border-color: var(--gray-300);
264
+ }
265
+
266
+ .card-header {
267
+ padding: var(--space-6);
268
+ border-bottom: 1px solid var(--gray-100);
269
+ background: var(--gray-50);
270
+ }
271
+
272
+ .card-title {
273
+ font-size: 1.25rem;
274
+ font-weight: 600;
275
+ color: var(--gray-900);
276
+ margin: 0;
277
+ }
278
+
279
+ .card-subtitle {
280
+ font-size: 0.875rem;
281
+ color: var(--gray-600);
282
+ margin-top: var(--space-1);
283
  }
284
 
285
+ .card-content {
286
+ padding: var(--space-6);
287
+ }
288
+
289
+ /* Form Sections */
290
  .form-section {
291
+ margin-bottom: var(--space-8);
292
+ }
293
+
294
+ .form-section:last-child {
295
+ margin-bottom: 0;
296
+ }
297
+
298
+ .section-header {
299
+ margin-bottom: var(--space-6);
300
  }
301
 
302
  .section-title {
303
  font-size: 1.125rem;
304
  font-weight: 600;
305
+ color: var(--gray-900);
306
+ margin: 0 0 var(--space-2) 0;
307
+ }
308
+
309
+ .section-description {
310
+ font-size: 0.875rem;
311
+ color: var(--gray-600);
312
+ margin: 0;
313
  }
314
 
315
+ /* Form Elements */
316
  .form-group {
317
+ margin-bottom: var(--space-5);
318
  }
319
 
320
  .form-row {
321
  display: grid;
322
  grid-template-columns: 1fr;
323
+ gap: var(--space-4);
324
  }
325
 
326
+ @media (min-width: 768px) {
327
  .form-row {
328
  grid-template-columns: 1fr 1fr;
329
  }
330
  }
331
 
332
+ .form-label {
333
  display: block;
 
 
 
334
  font-size: 0.875rem;
335
+ font-weight: 500;
336
+ color: var(--gray-700);
337
+ margin-bottom: var(--space-2);
338
+ }
339
+
340
+ .form-label.required::after {
341
+ content: '*';
342
+ color: var(--red-500);
343
+ margin-left: var(--space-1);
344
  }
345
 
346
+ .form-input {
347
  width: 100%;
348
+ padding: var(--space-3) var(--space-4);
 
 
349
  font-size: 0.875rem;
350
+ line-height: 1.25;
351
+ color: var(--gray-900);
352
  background: white;
353
+ border: 1px solid var(--gray-300);
354
+ border-radius: var(--radius-md);
355
+ transition: all 0.15s ease;
356
+ outline: none;
357
  }
358
 
359
+ .form-input:focus {
360
+ border-color: var(--primary-500);
361
+ box-shadow: 0 0 0 3px rgb(59 130 246 / 0.1);
 
362
  }
363
 
364
+ .form-input:disabled {
365
+ background: var(--gray-50);
366
+ color: var(--gray-500);
367
+ cursor: not-allowed;
368
+ }
369
+
370
+ .form-textarea {
371
  resize: vertical;
372
  min-height: 100px;
373
  }
374
 
375
+ .form-select {
376
+ background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 20 20'%3e%3cpath stroke='%236b7280' stroke-linecap='round' stroke-linejoin='round' stroke-width='1.5' d='m6 8 4 4 4-4'/%3e%3c/svg%3e");
377
+ background-position: right 0.5rem center;
378
+ background-repeat: no-repeat;
379
+ background-size: 1.5em 1.5em;
380
+ padding-right: 2.5rem;
381
+ appearance: none;
382
+ }
383
+
384
+ /* Multi-select */
385
  .multi-select {
386
+ border: 1px solid var(--gray-300);
387
+ border-radius: var(--radius-md);
 
 
 
388
  background: white;
389
+ max-height: 140px;
390
+ overflow-y: auto;
391
  }
392
 
393
+ .multi-select-option {
394
  display: flex;
395
  align-items: center;
396
+ padding: var(--space-3) var(--space-4);
397
+ border-bottom: 1px solid var(--gray-100);
398
+ transition: background-color 0.15s ease;
399
  cursor: pointer;
 
 
 
400
  }
401
 
402
+ .multi-select-option:last-child {
403
+ border-bottom: none;
404
  }
405
 
406
+ .multi-select-option:hover {
407
+ background: var(--gray-50);
 
408
  }
409
 
410
+ .multi-select-checkbox {
411
+ margin-right: var(--space-3);
412
+ width: 1rem;
413
+ height: 1rem;
414
+ accent-color: var(--primary-500);
 
 
 
 
415
  }
416
 
417
+ .multi-select-label {
418
+ flex: 1;
419
+ font-size: 0.875rem;
420
+ color: var(--gray-700);
421
+ margin: 0;
422
  }
423
 
424
+ /* File Upload */
425
+ .file-upload-area {
426
+ border: 2px dashed var(--gray-300);
427
+ border-radius: var(--radius-lg);
428
+ padding: var(--space-8);
429
+ text-align: center;
430
+ background: var(--gray-50);
431
+ transition: all 0.2s ease;
432
+ cursor: pointer;
 
433
  }
434
 
435
+ .file-upload-area:hover {
436
+ border-color: var(--primary-500);
437
+ background: var(--primary-50);
 
 
 
 
 
 
 
 
 
 
438
  }
439
 
440
+ .file-upload-area.dragover {
441
+ border-color: var(--primary-500);
442
+ background: var(--primary-50);
443
  }
444
 
445
+ .file-upload-icon {
446
+ font-size: 2rem;
447
+ margin-bottom: var(--space-2);
448
  }
449
 
450
+ .file-upload-text {
451
+ font-size: 0.875rem;
452
+ color: var(--gray-600);
453
  }
454
 
455
+ .file-upload-hint {
456
+ font-size: 0.75rem;
457
+ color: var(--gray-500);
458
+ margin-top: var(--space-1);
459
  }
460
 
461
+ /* Location Controls */
462
+ .location-group {
463
+ display: flex;
464
+ align-items: end;
465
+ gap: var(--space-3);
466
  }
467
 
468
+ .location-input {
469
+ flex: 1;
 
470
  }
471
 
472
+ .location-button {
473
+ flex-shrink: 0;
 
474
  }
475
 
476
+ /* Form Actions */
477
+ .form-actions {
478
  display: flex;
479
+ justify-content: flex-end;
480
+ gap: var(--space-3);
481
+ padding-top: var(--space-6);
482
+ border-top: 1px solid var(--gray-200);
483
+ margin-top: var(--space-8);
484
  }
485
 
486
+ @media (max-width: 640px) {
487
+ .form-actions {
488
+ flex-direction: column;
489
+ }
490
+
491
+ .btn {
492
+ width: 100%;
493
+ justify-content: center;
494
+ }
495
+ }
496
+
497
+ /* Messages */
498
+ .message {
499
+ padding: var(--space-4);
500
+ border-radius: var(--radius-md);
501
+ margin-bottom: var(--space-6);
502
  font-size: 0.875rem;
503
+ font-weight: 500;
504
+ border: 1px solid transparent;
505
  }
506
 
507
+ .message.success {
508
+ background: var(--green-50);
509
+ color: var(--green-600);
510
+ border-color: var(--green-500);
511
  }
512
 
513
+ .message.error {
514
+ background: var(--red-50);
515
+ color: var(--red-600);
516
+ border-color: var(--red-500);
517
  }
518
 
519
+ /* Sidebar */
520
  .sidebar-title {
521
  font-size: 1.125rem;
522
  font-weight: 600;
523
+ color: var(--gray-900);
524
+ margin-bottom: var(--space-4);
 
 
525
  }
526
 
527
  .tree-list {
528
+ max-height: 70vh;
529
  overflow-y: auto;
530
  }
531
 
532
  .tree-item {
533
+ padding: var(--space-4);
534
+ border: 1px solid var(--gray-200);
535
+ border-radius: var(--radius-md);
536
+ margin-bottom: var(--space-3);
537
  background: white;
538
+ transition: all 0.15s ease;
539
  }
540
 
541
  .tree-item:hover {
542
+ border-color: var(--primary-500);
543
+ box-shadow: var(--shadow-sm);
544
+ transform: translateY(-1px);
545
+ }
546
+
547
+ .tree-header {
548
+ display: flex;
549
+ justify-content: space-between;
550
+ align-items: center;
551
+ margin-bottom: var(--space-2);
552
  }
553
 
554
  .tree-id {
555
  font-weight: 600;
556
+ color: var(--primary-600);
557
  font-size: 0.875rem;
558
  }
559
 
560
+ .tree-actions {
 
 
 
 
 
 
 
 
 
 
 
 
 
561
  display: flex;
562
+ gap: var(--space-1);
 
 
563
  }
564
 
565
+ .btn-icon {
566
+ background: transparent;
 
567
  border: none;
 
 
 
 
568
  cursor: pointer;
569
+ padding: var(--space-1);
570
+ border-radius: var(--radius-sm);
571
+ color: var(--gray-500);
572
+ transition: all 0.15s ease;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
573
  display: flex;
574
+ align-items: center;
575
+ justify-content: center;
 
 
 
576
  }
577
 
578
+ .btn-icon:hover {
579
+ background: var(--gray-100);
580
+ color: var(--gray-700);
 
 
 
 
 
581
  }
582
 
583
+ .tree-info {
584
+ color: var(--gray-600);
 
 
 
 
585
  font-size: 0.75rem;
586
+ line-height: 1.4;
587
  }
588
 
589
+ /* Loading States */
590
+ .loading {
591
+ display: flex;
592
+ align-items: center;
593
+ justify-content: center;
594
+ padding: var(--space-8);
595
+ color: var(--gray-500);
596
  font-size: 0.875rem;
 
 
 
 
 
 
 
 
 
597
  }
598
 
599
+ .spinner {
600
+ border: 2px solid var(--gray-200);
601
+ border-top: 2px solid var(--primary-500);
602
+ border-radius: 50%;
603
+ width: 1rem;
604
+ height: 1rem;
605
+ animation: spin 1s linear infinite;
606
+ margin-right: var(--space-2);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
607
  }
608
 
609
+ @keyframes spin {
610
+ 0% { transform: rotate(0deg); }
611
+ 100% { transform: rotate(360deg); }
 
 
612
  }
613
 
614
+ /* Hidden */
615
+ .hidden {
616
+ display: none;
 
 
 
 
 
 
 
617
  }
618
 
619
+ /* Mobile Optimizations */
620
+ @media (max-width: 768px) {
621
+ .header-content {
622
+ padding: var(--space-3) var(--space-4);
623
+ }
624
+
625
+ .header h1 {
626
+ font-size: 1.5rem;
627
+ }
628
+
629
+ .main-container {
630
+ padding: var(--space-6) var(--space-4);
631
+ }
632
+
633
+ .card-header,
634
+ .card-content {
635
+ padding: var(--space-4);
636
+ }
637
+
638
+ .user-info {
639
+ padding: var(--space-2) var(--space-3);
640
+ }
641
+
642
+ .user-details {
643
+ display: none;
644
+ }
645
  }
646
+
647
+ /* Focus visible for accessibility */
648
+ .btn:focus-visible,
649
+ .form-input:focus-visible,
650
+ .btn-icon:focus-visible {
651
+ outline: 2px solid var(--primary-500);
652
+ outline-offset: 2px;
653
  }
654
  </style>
655
  <script>
656
  // Force refresh if we detect cached version
657
  (function() {
658
+ const currentVersion = '4.0.0';
659
  const lastVersion = sessionStorage.getItem('treetrack_version');
660
  if (!lastVersion || lastVersion !== currentVersion) {
661
  sessionStorage.setItem('treetrack_version', currentVersion);
 
670
  <body>
671
  <div class="header">
672
  <div class="header-content">
673
+ <div class="header-brand">
674
+ <h1>🌳 TreeTrack</h1>
675
+ <div class="header-subtitle">Professional Field Research Platform</div>
676
+ </div>
677
+ <div class="header-actions">
678
+ <div class="user-info" id="userInfo">
679
+ <div class="user-avatar" id="userAvatar">U</div>
680
+ <div class="user-details">
681
+ <div class="user-name" id="userName">Loading...</div>
682
+ <div class="user-role" id="userRole">User</div>
683
+ </div>
684
+ </div>
685
+ <a href="/static/map.html" class="btn btn-secondary">πŸ“ View Map</a>
686
+ <button id="logoutBtn" class="btn btn-secondary">Logout</button>
687
  </div>
 
688
  </div>
689
  </div>
690
 
691
+ <div class="main-container">
692
+ <div class="content-grid">
693
+ <div class="card">
694
+ <div class="card-header">
695
+ <h2 class="card-title">Tree Documentation Form</h2>
696
+ <p class="card-subtitle">Complete the form below to document tree specimens in the field. All required fields are marked with an asterisk.</p>
697
+ </div>
698
+ <div class="card-content">
699
+ <form id="treeForm">
700
+ <!-- Location Section -->
701
+ <div class="form-section">
702
+ <div class="section-header">
703
+ <h3 class="section-title">πŸ“ Geographic Location</h3>
704
+ <p class="section-description">Precise coordinates are essential for mapping and field research</p>
705
+ </div>
706
+
707
+ <div class="form-row">
708
+ <div class="form-group">
709
+ <label class="form-label required" for="latitude">Latitude</label>
710
+ <div class="location-group">
711
+ <div class="location-input">
712
+ <input type="number" id="latitude" class="form-input" step="0.0000001" min="-90" max="90" required placeholder="e.g. 26.1445">
713
+ </div>
714
+ <div class="location-button">
715
+ <button type="button" id="getLocation" class="btn btn-outline btn-sm">πŸ“ GPS</button>
716
+ </div>
717
+ </div>
718
+ </div>
719
+ <div class="form-group">
720
+ <label class="form-label required" for="longitude">Longitude</label>
721
+ <input type="number" id="longitude" class="form-input" step="0.0000001" min="-180" max="180" required placeholder="e.g. 91.7362">
722
+ </div>
723
+ </div>
724
+
725
+ <div class="form-group">
726
+ <a href="/static/map.html" class="btn btn-outline" style="width: 100%; justify-content: center;">πŸ—ΊοΈ Select from Interactive Map</a>
727
  </div>
728
  </div>
 
 
 
 
 
 
 
 
 
729
 
730
+ <!-- Identification Section -->
731
+ <div class="form-section">
732
+ <div class="section-header">
733
+ <h3 class="section-title">🌿 Tree Identification</h3>
734
+ <p class="section-description">Botanical and local identification details</p>
735
+ </div>
736
+
737
+ <div class="form-group">
738
+ <label class="form-label" for="localName">Local Name (Assamese)</label>
739
+ <input type="text" id="localName" class="form-input" placeholder="Enter local Assamese name">
740
+ </div>
741
+
742
+ <div class="form-group">
743
+ <label class="form-label" for="scientificName">Scientific Name</label>
744
+ <input type="text" id="scientificName" class="form-input" placeholder="e.g., Ficus benghalensis">
745
+ </div>
746
+
747
+ <div class="form-group">
748
+ <label class="form-label" for="commonName">Common Name</label>
749
+ <input type="text" id="commonName" class="form-input" placeholder="e.g., Banyan Tree">
750
+ </div>
751
+
752
+ <div class="form-group">
753
+ <label class="form-label" for="treeCode">Tree Reference Code</label>
754
+ <input type="text" id="treeCode" class="form-input" placeholder="e.g., C.A, A-G1" maxlength="20">
755
+ </div>
756
+ </div>
757
 
758
+ <!-- Measurements Section -->
759
+ <div class="form-section">
760
+ <div class="section-header">
761
+ <h3 class="section-title">πŸ“ Physical Measurements</h3>
762
+ <p class="section-description">Quantitative assessment of tree dimensions</p>
763
+ </div>
764
+
765
+ <div class="form-row">
766
+ <div class="form-group">
767
+ <label class="form-label" for="height">Height (meters)</label>
768
+ <input type="number" id="height" class="form-input" step="0.1" min="0" max="200" placeholder="15.5">
769
+ </div>
770
+ <div class="form-group">
771
+ <label class="form-label" for="width">Girth/DBH (cm)</label>
772
+ <input type="number" id="width" class="form-input" step="0.1" min="0" max="2000" placeholder="45.2">
773
+ </div>
774
+ </div>
775
  </div>
776
+
777
+ <!-- Utility Section -->
778
+ <div class="form-section">
779
+ <div class="section-header">
780
+ <h3 class="section-title">🌱 Ecological & Cultural Utility</h3>
781
+ <p class="section-description">Select all applicable ecological and cultural uses</p>
782
+ </div>
783
+
784
+ <div class="form-group">
785
+ <div id="utilityOptions" class="multi-select">
786
+ <!-- Options loaded dynamically -->
787
+ </div>
788
+ </div>
789
  </div>
 
 
790
 
791
+ <!-- Phenology Section -->
792
+ <div class="form-section">
793
+ <div class="section-header">
794
+ <h3 class="section-title">πŸƒ Phenology Assessment</h3>
795
+ <p class="section-description">Current developmental stages observed</p>
796
+ </div>
797
+
798
+ <div class="form-group">
799
+ <div id="phenologyOptions" class="multi-select">
800
+ <!-- Options loaded dynamically -->
801
+ </div>
802
+ </div>
803
  </div>
 
 
804
 
805
+ <!-- Photography Section -->
806
+ <div class="form-section">
807
+ <div class="section-header">
808
+ <h3 class="section-title">πŸ“· Photographic Documentation</h3>
809
+ <p class="section-description">Upload photographs for different tree components</p>
810
+ </div>
811
+
812
+ <div id="photoCategories">
813
+ <!-- Photo categories loaded dynamically -->
814
+ </div>
815
  </div>
 
 
816
 
817
+ <!-- Cultural Documentation Section -->
818
+ <div class="form-section">
819
+ <div class="section-header">
820
+ <h3 class="section-title">πŸ“– Cultural Documentation</h3>
821
+ <p class="section-description">Stories, histories, and cultural significance</p>
822
+ </div>
823
+
824
+ <div class="form-group">
825
+ <label class="form-label" for="storytellingText">Stories, Histories & Narratives</label>
826
+ <textarea id="storytellingText" class="form-input form-textarea" placeholder="Share any stories, historical context, or cultural significance..." maxlength="5000"></textarea>
827
+ </div>
828
+
829
+ <div class="form-group">
830
+ <label class="form-label">Audio Recording</label>
831
+ <div class="file-upload-area" id="audioUpload">
832
+ <div class="file-upload-icon">πŸŽ™οΈ</div>
833
+ <div class="file-upload-text">Click to upload audio file</div>
834
+ <div class="file-upload-hint">Or drag and drop (MP3, WAV, M4A)</div>
835
+ </div>
836
+ <div id="audioUploadResult"></div>
837
+ </div>
838
+ </div>
839
 
840
+ <!-- Field Notes Section -->
841
+ <div class="form-section">
842
+ <div class="section-header">
843
+ <h3 class="section-title">πŸ“ Field Notes</h3>
844
+ <p class="section-description">Additional observations and remarks</p>
845
+ </div>
846
+
847
+ <div class="form-group">
848
+ <label class="form-label" for="notes">Additional Observations</label>
849
+ <textarea id="notes" class="form-input form-textarea" placeholder="Any additional observations, notes, or remarks..." maxlength="2000"></textarea>
850
+ </div>
 
 
851
  </div>
852
+
853
+ <div class="form-actions">
854
+ <button type="button" id="resetForm" class="btn btn-outline">Reset Form</button>
855
+ <button type="submit" class="btn btn-primary btn-lg">πŸ’Ύ Save Tree Record</button>
856
  </div>
857
+ </form>
 
 
858
 
859
+ <div id="message"></div>
 
 
 
 
 
 
860
  </div>
861
+ </div>
862
 
863
+ <div class="card">
864
+ <div class="card-header">
865
+ <h3 class="card-title">Recent Trees</h3>
866
+ <p class="card-subtitle">Recently documented specimens</p>
867
+ </div>
868
+ <div class="card-content">
869
+ <div id="treeList" class="tree-list">
870
+ <div class="loading">
871
+ <div class="spinner"></div>
872
+ Loading trees...
873
+ </div>
874
+ </div>
875
  </div>
 
 
 
 
 
 
 
 
 
876
  </div>
877
  </div>
878
  </div>
879
 
880
+ <script src="/static/app.js?v=4.0.0&t=1754657582"></script>
881
  </body>
882
  </html>
static/index_old.html ADDED
@@ -0,0 +1,671 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate">
7
+ <meta http-equiv="Pragma" content="no-cache">
8
+ <meta http-equiv="Expires" content="0">
9
+ <title>TreeTrack - Field Research Tool</title>
10
+ <style>
11
+ @import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap');
12
+
13
+ * {
14
+ margin: 0;
15
+ padding: 0;
16
+ box-sizing: border-box;
17
+ }
18
+
19
+ body {
20
+ font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
21
+ line-height: 1.6;
22
+ color: #1e293b;
23
+ background: #f8fafc;
24
+ min-height: 100vh;
25
+ }
26
+
27
+ .header {
28
+ background: linear-gradient(135deg, #1e40af 0%, #3b82f6 100%);
29
+ color: white;
30
+ padding: 1.5rem 0;
31
+ margin-bottom: 2rem;
32
+ box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1);
33
+ }
34
+
35
+ .header-content {
36
+ max-width: 1200px;
37
+ margin: 0 auto;
38
+ padding: 0 1rem;
39
+ display: flex;
40
+ justify-content: space-between;
41
+ align-items: center;
42
+ flex-wrap: wrap;
43
+ gap: 1rem;
44
+ }
45
+
46
+ .header h1 {
47
+ font-size: 1.875rem;
48
+ font-weight: 600;
49
+ margin: 0;
50
+ letter-spacing: -0.025em;
51
+ }
52
+
53
+ .header-subtitle {
54
+ font-size: 0.875rem;
55
+ opacity: 0.9;
56
+ margin-top: 0.25rem;
57
+ font-weight: 400;
58
+ }
59
+
60
+ .map-link {
61
+ background: rgba(255, 255, 255, 0.1);
62
+ color: white;
63
+ padding: 0.5rem 1rem;
64
+ border-radius: 6px;
65
+ text-decoration: none;
66
+ font-weight: 500;
67
+ transition: background-color 0.2s;
68
+ border: 1px solid rgba(255, 255, 255, 0.2);
69
+ }
70
+
71
+ .map-link:hover {
72
+ background: rgba(255, 255, 255, 0.2);
73
+ }
74
+
75
+ .container {
76
+ max-width: 1200px;
77
+ margin: 0 auto;
78
+ padding: 0 1rem 2rem;
79
+ display: grid;
80
+ grid-template-columns: 1fr;
81
+ gap: 2rem;
82
+ }
83
+
84
+ @media (min-width: 1024px) {
85
+ .container {
86
+ grid-template-columns: 2fr 1fr;
87
+ }
88
+ }
89
+
90
+ .form-container {
91
+ background: white;
92
+ border-radius: 12px;
93
+ padding: 2rem;
94
+ box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px 0 rgba(0, 0, 0, 0.06);
95
+ border: 1px solid #e2e8f0;
96
+ }
97
+
98
+ .sidebar-container {
99
+ background: white;
100
+ border-radius: 12px;
101
+ padding: 1.5rem;
102
+ box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px 0 rgba(0, 0, 0, 0.06);
103
+ border: 1px solid #e2e8f0;
104
+ height: fit-content;
105
+ }
106
+
107
+ .form-section {
108
+ margin-bottom: 2rem;
109
+ padding: 1.5rem;
110
+ border: 1px solid #e2e8f0;
111
+ border-radius: 8px;
112
+ background: #fafbfc;
113
+ }
114
+
115
+ .section-title {
116
+ font-size: 1.125rem;
117
+ font-weight: 600;
118
+ color: #374151;
119
+ margin-bottom: 1rem;
120
+ padding-bottom: 0.5rem;
121
+ border-bottom: 1px solid #e2e8f0;
122
+ }
123
+
124
+ .form-group {
125
+ margin-bottom: 1.5rem;
126
+ }
127
+
128
+ .form-row {
129
+ display: grid;
130
+ grid-template-columns: 1fr;
131
+ gap: 1rem;
132
+ }
133
+
134
+ @media (min-width: 640px) {
135
+ .form-row {
136
+ grid-template-columns: 1fr 1fr;
137
+ }
138
+ }
139
+
140
+ label {
141
+ display: block;
142
+ margin-bottom: 0.5rem;
143
+ font-weight: 500;
144
+ color: #374151;
145
+ font-size: 0.875rem;
146
+ }
147
+
148
+ input, textarea, select {
149
+ width: 100%;
150
+ padding: 0.75rem;
151
+ border: 1px solid #d1d5db;
152
+ border-radius: 6px;
153
+ font-size: 0.875rem;
154
+ transition: all 0.2s;
155
+ background: white;
156
+ }
157
+
158
+ input:focus, textarea:focus, select:focus {
159
+ outline: none;
160
+ border-color: #3b82f6;
161
+ box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1);
162
+ }
163
+
164
+ textarea {
165
+ resize: vertical;
166
+ min-height: 100px;
167
+ }
168
+
169
+ .multi-select {
170
+ border: 1px solid #d1d5db;
171
+ border-radius: 6px;
172
+ padding: 0.75rem;
173
+ max-height: 120px;
174
+ overflow-y: auto;
175
+ background: white;
176
+ }
177
+
178
+ .multi-select label {
179
+ display: flex;
180
+ align-items: center;
181
+ margin-bottom: 0.5rem;
182
+ font-weight: normal;
183
+ cursor: pointer;
184
+ padding: 0.25rem;
185
+ border-radius: 4px;
186
+ transition: background-color 0.2s;
187
+ }
188
+
189
+ .multi-select label:hover {
190
+ background-color: #f3f4f6;
191
+ }
192
+
193
+ .multi-select input[type="checkbox"] {
194
+ width: auto;
195
+ margin-right: 0.5rem;
196
+ }
197
+
198
+ .file-upload {
199
+ border: 2px dashed #d1d5db;
200
+ border-radius: 6px;
201
+ padding: 1.5rem;
202
+ text-align: center;
203
+ cursor: pointer;
204
+ transition: all 0.2s;
205
+ background: #f9fafb;
206
+ margin-top: 0.5rem;
207
+ }
208
+
209
+ .file-upload:hover {
210
+ border-color: #3b82f6;
211
+ background: #f0f9ff;
212
+ }
213
+
214
+ .photo-category {
215
+ display: grid;
216
+ grid-template-columns: 1fr auto;
217
+ gap: 0.75rem;
218
+ align-items: center;
219
+ margin-bottom: 1rem;
220
+ padding: 1rem;
221
+ border: 1px solid #e2e8f0;
222
+ border-radius: 6px;
223
+ background: white;
224
+ }
225
+
226
+ .btn {
227
+ padding: 0.75rem 1.5rem;
228
+ border: none;
229
+ border-radius: 6px;
230
+ cursor: pointer;
231
+ font-size: 0.875rem;
232
+ font-weight: 500;
233
+ transition: all 0.2s;
234
+ text-decoration: none;
235
+ display: inline-flex;
236
+ align-items: center;
237
+ justify-content: center;
238
+ gap: 0.5rem;
239
+ }
240
+
241
+ .btn-primary {
242
+ background: #3b82f6;
243
+ color: white;
244
+ }
245
+
246
+ .btn-primary:hover {
247
+ background: #2563eb;
248
+ }
249
+
250
+ .btn-secondary {
251
+ background: #6b7280;
252
+ color: white;
253
+ }
254
+
255
+ .btn-secondary:hover {
256
+ background: #4b5563;
257
+ }
258
+
259
+ .btn-outline {
260
+ background: transparent;
261
+ color: #3b82f6;
262
+ border: 1px solid #3b82f6;
263
+ }
264
+
265
+ .btn-outline:hover {
266
+ background: #3b82f6;
267
+ color: white;
268
+ }
269
+
270
+ .btn-small {
271
+ padding: 0.5rem 1rem;
272
+ font-size: 0.75rem;
273
+ }
274
+
275
+ .current-location {
276
+ display: flex;
277
+ align-items: center;
278
+ gap: 0.5rem;
279
+ }
280
+
281
+ .success-message, .error-message {
282
+ padding: 0.75rem 1rem;
283
+ border-radius: 6px;
284
+ margin: 1rem 0;
285
+ font-weight: 500;
286
+ font-size: 0.875rem;
287
+ }
288
+
289
+ .success-message {
290
+ background: #dcfce7;
291
+ color: #166534;
292
+ border: 1px solid #bbf7d0;
293
+ }
294
+
295
+ .error-message {
296
+ background: #fee2e2;
297
+ color: #991b1b;
298
+ border: 1px solid #fecaca;
299
+ }
300
+
301
+ .sidebar-title {
302
+ font-size: 1.125rem;
303
+ font-weight: 600;
304
+ color: #374151;
305
+ margin-bottom: 1rem;
306
+ padding-bottom: 0.5rem;
307
+ border-bottom: 1px solid #e2e8f0;
308
+ }
309
+
310
+ .tree-list {
311
+ max-height: 60vh;
312
+ overflow-y: auto;
313
+ }
314
+
315
+ .tree-item {
316
+ padding: 1rem;
317
+ border: 1px solid #e2e8f0;
318
+ border-radius: 6px;
319
+ margin-bottom: 0.75rem;
320
+ background: white;
321
+ transition: all 0.2s;
322
+ }
323
+
324
+ .tree-item:hover {
325
+ box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1);
326
+ border-color: #3b82f6;
327
+ }
328
+
329
+ .tree-id {
330
+ font-weight: 600;
331
+ color: #1e40af;
332
+ font-size: 0.875rem;
333
+ }
334
+
335
+ .tree-info {
336
+ color: #6b7280;
337
+ font-size: 0.75rem;
338
+ margin-top: 0.25rem;
339
+ line-height: 1.4;
340
+ }
341
+
342
+ .loading {
343
+ text-align: center;
344
+ padding: 2rem;
345
+ color: #6b7280;
346
+ }
347
+
348
+ .audio-controls {
349
+ display: flex;
350
+ gap: 0.75rem;
351
+ align-items: center;
352
+ margin-top: 0.75rem;
353
+ }
354
+
355
+ .record-btn {
356
+ background: #ef4444;
357
+ color: white;
358
+ border: none;
359
+ border-radius: 50%;
360
+ width: 3rem;
361
+ height: 3rem;
362
+ font-size: 1.125rem;
363
+ cursor: pointer;
364
+ transition: all 0.2s;
365
+ }
366
+
367
+ .record-btn:hover {
368
+ background: #dc2626;
369
+ }
370
+
371
+ .record-btn.recording {
372
+ background: #10b981;
373
+ animation: pulse 1.5s infinite;
374
+ }
375
+
376
+ @keyframes pulse {
377
+ 0% { transform: scale(1); }
378
+ 50% { transform: scale(1.05); }
379
+ 100% { transform: scale(1); }
380
+ }
381
+
382
+ .hidden {
383
+ display: none;
384
+ }
385
+
386
+ .form-actions {
387
+ display: flex;
388
+ gap: 1rem;
389
+ justify-content: flex-end;
390
+ margin-top: 2rem;
391
+ padding-top: 1.5rem;
392
+ border-top: 1px solid #e2e8f0;
393
+ }
394
+
395
+ @media (max-width: 640px) {
396
+ .form-actions {
397
+ flex-direction: column;
398
+ }
399
+
400
+ .btn {
401
+ width: 100%;
402
+ }
403
+ }
404
+
405
+ .uploaded-file {
406
+ margin-top: 0.5rem;
407
+ padding: 0.5rem 0.75rem;
408
+ background: #f0fdf4;
409
+ border: 1px solid #bbf7d0;
410
+ border-radius: 4px;
411
+ font-size: 0.75rem;
412
+ color: #166534;
413
+ }
414
+
415
+ .form-description {
416
+ color: #6b7280;
417
+ font-size: 0.875rem;
418
+ margin-bottom: 2rem;
419
+ line-height: 1.5;
420
+ }
421
+
422
+ /* Auto-suggestion styles */
423
+ .autocomplete-container {
424
+ position: relative;
425
+ display: inline-block;
426
+ width: 100%;
427
+ }
428
+
429
+ .autocomplete-dropdown {
430
+ position: absolute;
431
+ top: 100%;
432
+ left: 0;
433
+ right: 0;
434
+ background: white;
435
+ border: 1px solid #d1d5db;
436
+ border-top: none;
437
+ border-radius: 0 0 6px 6px;
438
+ box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1);
439
+ max-height: 200px;
440
+ overflow-y: auto;
441
+ z-index: 1000;
442
+ display: none;
443
+ }
444
+
445
+ .autocomplete-item {
446
+ padding: 0.75rem;
447
+ cursor: pointer;
448
+ border-bottom: 1px solid #f3f4f6;
449
+ transition: background-color 0.2s;
450
+ }
451
+
452
+ .autocomplete-item:last-child {
453
+ border-bottom: none;
454
+ }
455
+
456
+ .autocomplete-item:hover,
457
+ .autocomplete-item.highlighted {
458
+ background-color: #f3f4f6;
459
+ }
460
+
461
+ .autocomplete-primary {
462
+ font-weight: 500;
463
+ color: #1e293b;
464
+ font-size: 0.875rem;
465
+ }
466
+
467
+ .autocomplete-secondary {
468
+ font-size: 0.75rem;
469
+ color: #6b7280;
470
+ margin-top: 0.25rem;
471
+ line-height: 1.3;
472
+ }
473
+
474
+ .autocomplete-badge {
475
+ display: inline-block;
476
+ background: #e0f2fe;
477
+ color: #0369a1;
478
+ font-size: 0.625rem;
479
+ font-weight: 500;
480
+ padding: 0.125rem 0.375rem;
481
+ border-radius: 4px;
482
+ margin-top: 0.25rem;
483
+ margin-right: 0.25rem;
484
+ }
485
+
486
+ .autocomplete-loading {
487
+ padding: 0.75rem;
488
+ text-align: center;
489
+ color: #6b7280;
490
+ font-size: 0.75rem;
491
+ font-style: italic;
492
+ }
493
+
494
+ .autocomplete-no-results {
495
+ padding: 0.75rem;
496
+ text-align: center;
497
+ color: #9ca3af;
498
+ font-size: 0.75rem;
499
+ font-style: italic;
500
+ }
501
+ </style>
502
+ <script>
503
+ // Force refresh if we detect cached version
504
+ (function() {
505
+ const currentVersion = '3.1754653728';
506
+ const lastVersion = sessionStorage.getItem('treetrack_version');
507
+ if (!lastVersion || lastVersion !== currentVersion) {
508
+ sessionStorage.setItem('treetrack_version', currentVersion);
509
+ if (lastVersion && lastVersion !== currentVersion) {
510
+ location.reload(true);
511
+ return;
512
+ }
513
+ }
514
+ })();
515
+ </script>
516
+ </head>
517
+ <body>
518
+ <div class="header">
519
+ <div class="header-content">
520
+ <div>
521
+ <h1>TreeTrack</h1>
522
+ <div class="header-subtitle">Professional Field Research & Documentation</div>
523
+ </div>
524
+ <a href="/static/map.html" class="map-link">View Map</a>
525
+ </div>
526
+ </div>
527
+
528
+ <div class="container">
529
+ <div class="form-container">
530
+ <div class="form-description">
531
+ Complete the form below to document tree specimens in the field. All fields marked with * are required.
532
+ </div>
533
+
534
+ <form id="treeForm">
535
+ <!-- Section 1: Location -->
536
+ <div class="form-section">
537
+ <h3 class="section-title">Geographic Location</h3>
538
+ <div class="form-row">
539
+ <div class="form-group">
540
+ <label for="latitude">Latitude *</label>
541
+ <div class="current-location">
542
+ <input type="number" id="latitude" step="0.0000001" min="-90" max="90" required>
543
+ <button type="button" id="getLocation" class="btn btn-outline btn-small">Get GPS</button>
544
+ </div>
545
+ </div>
546
+ <div class="form-group">
547
+ <label for="longitude">Longitude *</label>
548
+ <input type="number" id="longitude" step="0.0000001" min="-180" max="180" required>
549
+ </div>
550
+ </div>
551
+ <div class="form-group">
552
+ <a href="/static/map.html" class="btn btn-outline" style="width: 100%; text-align: center;">Select from Interactive Map</a>
553
+ </div>
554
+ </div>
555
+
556
+ <!-- Section 2: Identification -->
557
+ <div class="form-section">
558
+ <h3 class="section-title">Tree Identification</h3>
559
+ <div class="form-group">
560
+ <label for="localName">Local Name (Assamese)</label>
561
+ <input type="text" id="localName" placeholder="Enter local Assamese name">
562
+ </div>
563
+ <div class="form-group">
564
+ <label for="scientificName">Scientific Name</label>
565
+ <input type="text" id="scientificName" placeholder="e.g., Ficus benghalensis">
566
+ </div>
567
+ <div class="form-group">
568
+ <label for="commonName">Common Name</label>
569
+ <input type="text" id="commonName" placeholder="e.g., Banyan Tree">
570
+ </div>
571
+ <div class="form-group">
572
+ <label for="treeCode">Tree Reference Code</label>
573
+ <input type="text" id="treeCode" placeholder="e.g., C.A, A-G1" maxlength="20">
574
+ </div>
575
+ </div>
576
+
577
+ <!-- Section 3: Measurements -->
578
+ <div class="form-section">
579
+ <h3 class="section-title">Physical Measurements</h3>
580
+ <div class="form-row">
581
+ <div class="form-group">
582
+ <label for="height">Height (meters)</label>
583
+ <input type="number" id="height" step="0.1" min="0" max="200" placeholder="15.5">
584
+ </div>
585
+ <div class="form-group">
586
+ <label for="width">Girth/DBH (cm)</label>
587
+ <input type="number" id="width" step="0.1" min="0" max="2000" placeholder="45.2">
588
+ </div>
589
+ </div>
590
+ </div>
591
+
592
+ <!-- Section 4: Utility -->
593
+ <div class="form-section">
594
+ <h3 class="section-title">Ecological & Cultural Utility</h3>
595
+ <div class="form-group">
596
+ <label>Select applicable utilities:</label>
597
+ <div id="utilityOptions" class="multi-select">
598
+ <!-- Options loaded dynamically -->
599
+ </div>
600
+ </div>
601
+ </div>
602
+
603
+ <!-- Section 5: Phenology -->
604
+ <div class="form-section">
605
+ <h3 class="section-title">Phenology Assessment</h3>
606
+ <div class="form-group">
607
+ <label>Current development stages:</label>
608
+ <div id="phenologyOptions" class="multi-select">
609
+ <!-- Options loaded dynamically -->
610
+ </div>
611
+ </div>
612
+ </div>
613
+
614
+ <!-- Section 6: Photography -->
615
+ <div class="form-section">
616
+ <h3 class="section-title">Photographic Documentation</h3>
617
+ <div id="photoCategories">
618
+ <!-- Photo categories loaded dynamically -->
619
+ </div>
620
+ </div>
621
+
622
+ <!-- Section 7: Storytelling -->
623
+ <div class="form-section">
624
+ <h3 class="section-title">Cultural Documentation</h3>
625
+ <div class="form-group">
626
+ <label for="storytellingText">Stories, Histories & Narratives</label>
627
+ <textarea id="storytellingText" placeholder="Share any stories, historical context, or cultural significance..." maxlength="5000"></textarea>
628
+ </div>
629
+ <div class="form-group">
630
+ <label>Audio Recording</label>
631
+ <div class="audio-controls">
632
+ <button type="button" id="recordBtn" class="record-btn" title="Record Audio">●</button>
633
+ <span id="recordingStatus">Click to start recording</span>
634
+ <audio id="audioPlayback" controls class="hidden"></audio>
635
+ </div>
636
+ <div class="file-upload" id="audioUpload">
637
+ Click to upload audio file or drag and drop
638
+ </div>
639
+ <div id="audioUploadResult"></div>
640
+ </div>
641
+ </div>
642
+
643
+ <!-- Section 8: Notes -->
644
+ <div class="form-section">
645
+ <h3 class="section-title">Field Notes</h3>
646
+ <div class="form-group">
647
+ <label for="notes">Additional Observations</label>
648
+ <textarea id="notes" placeholder="Any additional observations, notes, or remarks..." maxlength="2000"></textarea>
649
+ </div>
650
+ </div>
651
+
652
+ <div class="form-actions">
653
+ <button type="button" id="resetForm" class="btn btn-secondary">Reset Form</button>
654
+ <button type="submit" class="btn btn-primary">Save Tree Record</button>
655
+ </div>
656
+ </form>
657
+
658
+ <div id="message"></div>
659
+ </div>
660
+
661
+ <div class="sidebar-container">
662
+ <h3 class="sidebar-title">Recent Trees</h3>
663
+ <div id="treeList" class="tree-list">
664
+ <div class="loading">Loading trees...</div>
665
+ </div>
666
+ </div>
667
+ </div>
668
+
669
+ <script src="/static/app.js?v=3.1754653728&t=1754653728"></script>
670
+ </body>
671
+ </html>
static/login.html CHANGED
@@ -272,19 +272,19 @@
272
  <div class="demo-accounts">
273
  <div class="demo-title">πŸ” Available Accounts</div>
274
  <div class="account-list">
275
- <div class="account-item" onclick="fillCredentials('aalekh')">
276
  <div class="account-role">Aalekh (Admin)</div>
277
  <div class="account-username">Full system access</div>
278
  </div>
279
- <div class="account-item" onclick="fillCredentials('admin')">
280
  <div class="account-role">System Admin</div>
281
  <div class="account-username">Administrative access</div>
282
  </div>
283
- <div class="account-item" onclick="fillCredentials('ishita')">
284
  <div class="account-role">Ishita</div>
285
  <div class="account-username">Tree research & documentation</div>
286
  </div>
287
- <div class="account-item" onclick="fillCredentials('jeeb')">
288
  <div class="account-role">Jeeb</div>
289
  <div class="account-username">Tree research & documentation</div>
290
  </div>
@@ -300,7 +300,7 @@
300
  </div>
301
 
302
  <script>
303
- function fillCredentials(username) {
304
  document.getElementById('username').value = username;
305
  document.getElementById('password').value = '';
306
  document.getElementById('password').focus();
@@ -308,7 +308,20 @@
308
  // Add visual feedback
309
  const accountItems = document.querySelectorAll('.account-item');
310
  accountItems.forEach(item => item.style.background = 'rgba(255, 255, 255, 0.7)');
311
- event.target.closest('.account-item').style.background = 'rgba(59, 130, 246, 0.1)';
 
 
 
 
 
 
 
 
 
 
 
 
 
312
  }
313
 
314
  function showMessage(message, type = 'error') {
 
272
  <div class="demo-accounts">
273
  <div class="demo-title">πŸ” Available Accounts</div>
274
  <div class="account-list">
275
+ <div class="account-item" onclick="fillCredentials('aalekh', event)">
276
  <div class="account-role">Aalekh (Admin)</div>
277
  <div class="account-username">Full system access</div>
278
  </div>
279
+ <div class="account-item" onclick="fillCredentials('admin', event)">
280
  <div class="account-role">System Admin</div>
281
  <div class="account-username">Administrative access</div>
282
  </div>
283
+ <div class="account-item" onclick="fillCredentials('ishita', event)">
284
  <div class="account-role">Ishita</div>
285
  <div class="account-username">Tree research & documentation</div>
286
  </div>
287
+ <div class="account-item" onclick="fillCredentials('jeeb', event)">
288
  <div class="account-role">Jeeb</div>
289
  <div class="account-username">Tree research & documentation</div>
290
  </div>
 
300
  </div>
301
 
302
  <script>
303
+ function fillCredentials(username, event = null) {
304
  document.getElementById('username').value = username;
305
  document.getElementById('password').value = '';
306
  document.getElementById('password').focus();
 
308
  // Add visual feedback
309
  const accountItems = document.querySelectorAll('.account-item');
310
  accountItems.forEach(item => item.style.background = 'rgba(255, 255, 255, 0.7)');
311
+
312
+ // Only try to highlight if event is provided
313
+ if (event && event.target) {
314
+ const targetItem = event.target.closest('.account-item');
315
+ if (targetItem) {
316
+ targetItem.style.background = 'rgba(59, 130, 246, 0.1)';
317
+ }
318
+ } else {
319
+ // If no event, try to find the account item by username
320
+ const targetItem = document.querySelector(`[onclick="fillCredentials('${username}')"]`);
321
+ if (targetItem) {
322
+ targetItem.style.background = 'rgba(59, 130, 246, 0.1)';
323
+ }
324
+ }
325
  }
326
 
327
  function showMessage(message, type = 'error') {
static/map.html CHANGED
@@ -12,6 +12,62 @@
12
  <link rel="stylesheet" href="https://unpkg.com/[email protected]/dist/leaflet.css" />
13
 
14
  <style>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
15
  * {
16
  margin: 0;
17
  padding: 0;
@@ -19,11 +75,15 @@
19
  }
20
 
21
  body {
22
- font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
23
- background: #1a1a1a;
24
- color: white;
 
 
25
  height: 100vh;
26
  overflow: hidden;
 
 
27
  }
28
 
29
  .app-container {
@@ -34,63 +94,154 @@
34
 
35
  /* Header */
36
  .header {
37
- background: linear-gradient(135deg, #1e40af 0%, #3b82f6 100%) !important;
38
- padding: 1rem 1.5rem;
 
 
 
 
 
 
 
39
  display: flex;
40
  justify-content: space-between;
41
  align-items: center;
42
- box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1);
43
- z-index: 1000;
44
- /* Red border removed after debugging */
 
 
 
 
 
 
45
  }
46
 
47
  .logo {
48
  font-size: 1.5rem;
49
- font-weight: 600;
 
 
 
 
50
  display: flex;
51
  align-items: center;
52
- gap: 10px;
53
- letter-spacing: -0.025em;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
54
  }
55
 
56
  .header-actions {
57
  display: flex;
58
- gap: 10px;
59
  align-items: center;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
60
  }
61
 
62
  .btn {
63
- padding: 8px 15px;
 
 
 
 
 
 
 
 
64
  border: none;
65
- border-radius: 20px;
66
  cursor: pointer;
67
- font-size: 14px;
68
- font-weight: 600;
69
- transition: all 0.3s ease;
70
  text-decoration: none;
71
- display: inline-flex;
72
- align-items: center;
73
- gap: 5px;
74
  }
75
 
76
  .btn-primary {
77
- background: #3b82f6;
78
  color: white;
79
  }
80
 
81
  .btn-primary:hover {
82
- background: #2563eb;
83
  transform: translateY(-1px);
 
84
  }
85
 
86
  .btn-secondary {
87
- background: rgba(255,255,255,0.2);
88
  color: white;
89
- backdrop-filter: blur(10px);
90
  }
91
 
92
  .btn-secondary:hover {
93
- background: rgba(255,255,255,0.3);
 
 
 
 
 
 
94
  }
95
 
96
  /* Map Container */
@@ -106,194 +257,202 @@
106
  z-index: 1;
107
  }
108
 
109
- /* Floating Controls */
110
- .floating-controls {
111
  position: absolute;
112
- top: 20px;
113
- right: 20px;
114
  z-index: 1000;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
115
  display: flex;
116
  flex-direction: column;
117
- gap: 10px;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
118
  }
119
 
120
- .control-panel {
121
- background: rgba(0,0,0,0.8);
122
- backdrop-filter: blur(10px);
123
- border-radius: 15px;
124
- padding: 15px;
125
- box-shadow: 0 8px 32px rgba(0,0,0,0.3);
126
- border: 1px solid rgba(255,255,255,0.1);
127
  }
128
 
129
- .location-info {
130
- position: absolute;
131
- bottom: 20px;
132
- left: 20px;
133
- right: 20px;
134
- z-index: 1000;
135
  }
136
 
137
- .info-panel {
138
- background: rgba(0,0,0,0.9);
139
- backdrop-filter: blur(15px);
140
- border-radius: 15px;
141
- padding: 20px;
142
- box-shadow: 0 8px 32px rgba(0,0,0,0.5);
143
- border: 1px solid rgba(255,255,255,0.1);
 
 
 
 
 
144
  transform: translateY(100%);
145
  transition: transform 0.3s ease;
146
  }
147
 
148
- .info-panel.active {
149
  transform: translateY(0);
150
  }
151
 
152
- .coordinates {
153
- display: flex;
154
- gap: 20px;
155
- margin-bottom: 15px;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
156
  }
157
 
158
  .coord-item {
159
- flex: 1;
160
  text-align: center;
161
  }
162
 
163
  .coord-label {
164
- font-size: 12px;
165
- opacity: 0.7;
166
- margin-bottom: 5px;
 
 
 
167
  }
168
 
169
  .coord-value {
170
- font-size: 18px;
171
- font-weight: bold;
172
- color: #3b82f6;
 
173
  }
174
 
175
- .quick-actions {
176
- display: flex;
177
- gap: 10px;
178
- margin-top: 15px;
179
  }
180
 
181
- .quick-actions .btn {
182
- flex: 1;
 
 
 
 
 
 
 
 
 
 
 
 
183
  }
184
 
185
- /* Tree Markers Info */
186
- .tree-counter {
187
- background: rgba(59, 130, 246, 0.9);
188
- color: white;
189
- padding: 10px 15px;
190
- border-radius: 20px;
191
- font-weight: bold;
192
- display: flex;
193
- align-items: center;
194
- gap: 8px;
195
  }
196
 
197
- /* Mobile Optimizations */
198
- @media (max-width: 768px) {
199
- .header {
200
- padding: 10px 15px;
201
- }
202
-
203
- .logo {
204
- font-size: 1.3rem;
205
- }
206
-
207
- .floating-controls {
208
- top: 10px;
209
- right: 10px;
210
- }
211
-
212
- .control-panel {
213
- padding: 10px;
214
- }
215
-
216
- .location-info {
217
- bottom: 10px;
218
- left: 10px;
219
- right: 10px;
220
- }
221
-
222
- .coordinates {
223
- flex-direction: column;
224
- gap: 10px;
225
- }
226
-
227
- .quick-actions {
228
- flex-direction: column;
229
- }
230
-
231
- .btn {
232
- padding: 12px 20px;
233
- font-size: 16px;
234
- }
235
  }
236
 
237
- /* Custom Pin Styles */
238
- .tree-pin {
239
- background: #3b82f6;
240
- border: 3px solid white;
241
- border-radius: 50%;
242
- box-shadow: 0 2px 10px rgba(0,0,0,0.3);
243
  }
244
 
245
- .temp-pin {
246
- background: #ff6b35;
247
- border: 3px solid white;
248
- border-radius: 50%;
249
- box-shadow:
250
- 0 2px 10px rgba(0,0,0,0.3),
251
- 0 0 15px rgba(255, 107, 53, 0.6),
252
- 0 0 25px rgba(255, 107, 53, 0.3);
253
- animation: gentle-glow 3s ease-in-out infinite;
254
- }
255
-
256
- @keyframes gentle-glow {
257
- 0% {
258
- box-shadow:
259
- 0 2px 10px rgba(0,0,0,0.3),
260
- 0 0 15px rgba(255, 107, 53, 0.6),
261
- 0 0 25px rgba(255, 107, 53, 0.3);
262
- }
263
- 50% {
264
- box-shadow:
265
- 0 2px 12px rgba(0,0,0,0.4),
266
- 0 0 20px rgba(255, 107, 53, 0.8),
267
- 0 0 35px rgba(255, 107, 53, 0.5);
268
- }
269
- 100% {
270
- box-shadow:
271
- 0 2px 10px rgba(0,0,0,0.3),
272
- 0 0 15px rgba(255, 107, 53, 0.6),
273
- 0 0 25px rgba(255, 107, 53, 0.3);
274
- }
275
  }
276
 
277
- /* Loading States */
278
  .loading {
279
  position: absolute;
280
  top: 50%;
281
  left: 50%;
282
  transform: translate(-50%, -50%);
283
- background: rgba(0,0,0,0.8);
284
- padding: 20px 30px;
285
- border-radius: 10px;
 
286
  z-index: 2000;
 
 
 
 
 
287
  }
288
 
289
  .spinner {
290
- border: 3px solid rgba(255,255,255,0.3);
291
- border-top: 3px solid #3b82f6;
292
  border-radius: 50%;
293
- width: 30px;
294
- height: 30px;
295
  animation: spin 1s linear infinite;
296
- margin: 0 auto 10px;
297
  }
298
 
299
  @keyframes spin {
@@ -301,176 +460,230 @@
301
  100% { transform: rotate(360deg); }
302
  }
303
 
304
- /* Success/Error Messages */
305
- .message {
306
- position: absolute;
307
- top: 80px;
308
- left: 50%;
309
- transform: translateX(-50%);
310
- padding: 15px 25px;
311
- border-radius: 25px;
312
- font-weight: 600;
313
- z-index: 1500;
314
- opacity: 0;
315
- transition: opacity 0.3s ease;
316
  }
317
 
318
- .message.show {
319
- opacity: 1;
 
320
  }
321
 
322
- .message.success {
323
- background: linear-gradient(45deg, #3b82f6, #2563eb);
324
- color: white;
 
 
325
  }
326
 
327
- .message.error {
328
- background: linear-gradient(45deg, #f44336, #d32f2f);
329
- color: white;
330
  }
331
 
332
- /* Gesture Instructions */
333
- .gesture-hint {
334
- position: absolute;
335
- bottom: 120px;
336
- left: 50%;
337
- transform: translateX(-50%);
338
- background: rgba(0,0,0,0.7);
339
- color: white;
340
- padding: 10px 20px;
341
- border-radius: 20px;
342
- font-size: 14px;
343
- z-index: 1000;
344
- animation: fadeInOut 4s ease-in-out;
345
- }
346
-
347
- @keyframes fadeInOut {
348
- 0%, 100% { opacity: 0; }
349
- 20%, 80% { opacity: 1; }
350
  }
351
 
352
- /* Custom Tree Marker Styles */
353
- .custom-tree-icon {
354
- background: transparent !important;
355
- border: none !important;
356
  }
357
 
358
- .custom-tree-marker {
359
- position: relative;
360
  display: flex;
361
- flex-direction: column;
362
  align-items: center;
 
 
 
 
363
  }
364
 
365
- .tree-icon-container {
366
- background: linear-gradient(145deg, #ffffff, #f0f0f0);
367
- border-radius: 50%;
368
- padding: 4px;
369
- box-shadow:
370
- 0 4px 8px rgba(0,0,0,0.15),
371
- 0 2px 4px rgba(0,0,0,0.1),
372
- inset 0 1px 0 rgba(255,255,255,0.2);
373
- transition: all 0.3s ease;
374
- cursor: pointer;
375
  }
376
 
377
- .tree-icon-container:hover {
378
- transform: translateY(-2px) scale(1.1);
379
- box-shadow:
380
- 0 6px 16px rgba(0,0,0,0.2),
381
- 0 4px 8px rgba(0,0,0,0.15),
382
- inset 0 1px 0 rgba(255,255,255,0.3);
383
  }
384
 
385
- .tree-marker-shadow {
386
- width: 12px;
387
- height: 6px;
388
- background: rgba(0,0,0,0.3);
389
- border-radius: 50%;
390
- margin-top: 2px;
391
- filter: blur(1px);
392
- transition: all 0.3s ease;
393
  }
394
 
395
- .custom-tree-marker:hover .tree-marker-shadow {
396
- width: 16px;
397
- background: rgba(0,0,0,0.4);
 
 
398
  }
399
 
400
- /* Tooltip Styles */
401
- .leaflet-tooltip.tree-tooltip {
402
- background: linear-gradient(145deg, #1e40af, #1d4ed8) !important;
403
- border: 1px solid rgba(255,255,255,0.2) !important;
404
- border-radius: 8px !important;
405
- box-shadow: 0 4px 12px rgba(0,0,0,0.3) !important;
406
- color: white !important;
407
- font-family: 'Segoe UI', sans-serif !important;
408
- font-size: 13px !important;
409
- padding: 8px 12px !important;
410
- backdrop-filter: blur(10px);
411
  }
412
 
413
- .leaflet-tooltip.tree-tooltip::before {
414
- border-top-color: #1e40af !important;
 
 
 
415
  }
416
 
417
- .tree-tooltip-content {
418
- min-width: 80px;
419
- text-align: center;
420
  }
421
 
422
- .tree-name {
 
423
  font-weight: 600;
424
- font-size: 14px;
425
- margin-bottom: 2px;
426
- color: #ffffff;
427
  }
428
 
429
- .tree-details {
430
- font-size: 11px;
431
- opacity: 0.9;
432
- color: #e8f5e8;
 
 
 
 
 
 
 
 
 
 
 
 
 
433
  }
434
 
435
- /* Enhanced Popup Styles */
436
- .leaflet-popup.tree-popup {
437
- margin-bottom: 10px;
438
  }
439
 
440
- .leaflet-popup.tree-popup .leaflet-popup-content-wrapper {
441
- background: linear-gradient(145deg, #ffffff, #f8f9fa);
442
- border-radius: 12px;
443
- box-shadow: 0 8px 24px rgba(0,0,0,0.15);
444
- border: 1px solid rgba(44, 85, 48, 0.1);
445
  }
446
 
447
- .leaflet-popup.tree-popup .leaflet-popup-tip {
448
- background: #ffffff;
449
- border: 1px solid rgba(59, 130, 246, 0.1);
450
- box-shadow: 0 2px 4px rgba(0,0,0,0.1);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
451
  }
452
  </style>
453
  <script>
454
  // Force refresh if we detect cached version
455
  (function() {
456
- const currentVersion = '3.1754653904';
457
  const lastVersion = sessionStorage.getItem('treetrack_version');
458
-
459
- // Always reload on version mismatch
460
- if (lastVersion && lastVersion !== currentVersion) {
461
- console.log('Version changed from', lastVersion, 'to', currentVersion, '- forcing reload');
462
  sessionStorage.setItem('treetrack_version', currentVersion);
463
- location.reload(true);
464
- return;
 
 
465
  }
466
-
467
- // Set version if not set
468
- if (!lastVersion) {
469
- sessionStorage.setItem('treetrack_version', currentVersion);
470
- }
471
-
472
- // Add debug info to page title
473
- document.title = 'TreeTrack Map - ' + new Date().toLocaleTimeString();
474
  })();
475
  </script>
476
  </head>
@@ -478,15 +691,35 @@
478
  <div class="app-container">
479
  <!-- Header -->
480
  <div class="header">
481
- <div class="logo">
482
- πŸ”₯ TreeTrack Map V4.0 πŸ”₯
483
- </div>
484
- <div class="header-actions">
485
- <div class="tree-counter">
486
- <span>Trees:</span>
487
- <span id="treeCount">0</span>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
488
  </div>
489
- <a href="/static/index.html" class="btn btn-secondary">Add Tree</a>
490
  </div>
491
  </div>
492
 
@@ -494,18 +727,36 @@
494
  <div class="map-container">
495
  <div id="map"></div>
496
 
497
- <!-- Floating Controls -->
498
- <div class="floating-controls">
499
- <div class="control-panel">
500
- <button id="myLocationBtn" class="btn btn-primary">My Location</button>
501
- <button id="clearPinsBtn" class="btn btn-secondary" style="margin-top: 8px;">Clear Pins</button>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
502
  </div>
503
  </div>
504
 
505
- <!-- Location Info Panel -->
506
- <div class="location-info">
507
- <div class="info-panel" id="infoPanel">
508
- <div class="coordinates">
 
 
 
509
  <div class="coord-item">
510
  <div class="coord-label">Latitude</div>
511
  <div class="coord-value" id="latValue">--</div>
@@ -515,9 +766,13 @@
515
  <div class="coord-value" id="lngValue">--</div>
516
  </div>
517
  </div>
518
- <div class="quick-actions">
519
- <button id="useLocationBtn" class="btn btn-primary">Use This Location</button>
520
- <button id="cancelBtn" class="btn btn-secondary">Cancel</button>
 
 
 
 
521
  </div>
522
  </div>
523
  </div>
@@ -525,7 +780,7 @@
525
  <!-- Loading -->
526
  <div id="loading" class="loading" style="display: none;">
527
  <div class="spinner"></div>
528
- <div>Loading map...</div>
529
  </div>
530
 
531
  <!-- Messages -->
@@ -533,13 +788,13 @@
533
 
534
  <!-- Gesture Hint -->
535
  <div class="gesture-hint">
536
- Tap anywhere on map to drop a pin
537
  </div>
538
  </div>
539
  </div>
540
 
541
  <!-- Leaflet JS -->
542
  <script src="https://unpkg.com/[email protected]/dist/leaflet.js"></script>
543
- <script src="/static/map.js?v=3.1754653904&t=1754653904"></script>
544
  </body>
545
  </html>
 
12
  <link rel="stylesheet" href="https://unpkg.com/[email protected]/dist/leaflet.css" />
13
 
14
  <style>
15
+ @import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700;800&display=swap');
16
+
17
+ :root {
18
+ /* Colors */
19
+ --primary-50: #eff6ff;
20
+ --primary-100: #dbeafe;
21
+ --primary-500: #3b82f6;
22
+ --primary-600: #2563eb;
23
+ --primary-700: #1d4ed8;
24
+ --primary-900: #1e3a8a;
25
+
26
+ --gray-50: #f8fafc;
27
+ --gray-100: #f1f5f9;
28
+ --gray-200: #e2e8f0;
29
+ --gray-300: #cbd5e1;
30
+ --gray-400: #94a3b8;
31
+ --gray-500: #64748b;
32
+ --gray-600: #475569;
33
+ --gray-700: #334155;
34
+ --gray-800: #1e293b;
35
+ --gray-900: #0f172a;
36
+
37
+ --green-50: #f0fdf4;
38
+ --green-500: #22c55e;
39
+ --green-600: #16a34a;
40
+
41
+ --red-50: #fef2f2;
42
+ --red-500: #ef4444;
43
+ --red-600: #dc2626;
44
+
45
+ --orange-500: #f97316;
46
+ --orange-600: #ea580c;
47
+
48
+ /* Spacing */
49
+ --space-1: 0.25rem;
50
+ --space-2: 0.5rem;
51
+ --space-3: 0.75rem;
52
+ --space-4: 1rem;
53
+ --space-5: 1.25rem;
54
+ --space-6: 1.5rem;
55
+ --space-8: 2rem;
56
+
57
+ /* Radius */
58
+ --radius-sm: 0.375rem;
59
+ --radius-md: 0.5rem;
60
+ --radius-lg: 0.75rem;
61
+ --radius-xl: 1rem;
62
+ --radius-2xl: 1.5rem;
63
+
64
+ /* Shadows */
65
+ --shadow-sm: 0 1px 2px 0 rgb(0 0 0 / 0.05);
66
+ --shadow-md: 0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1);
67
+ --shadow-lg: 0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1);
68
+ --shadow-xl: 0 20px 25px -5px rgb(0 0 0 / 0.1), 0 8px 10px -6px rgb(0 0 0 / 0.1);
69
+ }
70
+
71
  * {
72
  margin: 0;
73
  padding: 0;
 
75
  }
76
 
77
  body {
78
+ font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
79
+ font-feature-settings: 'cv11' 1, 'ss01' 1;
80
+ line-height: 1.6;
81
+ color: var(--gray-800);
82
+ background: var(--gray-50);
83
  height: 100vh;
84
  overflow: hidden;
85
+ -webkit-font-smoothing: antialiased;
86
+ -moz-osx-font-smoothing: grayscale;
87
  }
88
 
89
  .app-container {
 
94
 
95
  /* Header */
96
  .header {
97
+ background: linear-gradient(135deg, var(--primary-600) 0%, var(--primary-700) 100%);
98
+ color: white;
99
+ z-index: 1000;
100
+ border-bottom: 1px solid rgba(255, 255, 255, 0.1);
101
+ backdrop-filter: blur(12px);
102
+ }
103
+
104
+ .header-content {
105
+ padding: var(--space-4) var(--space-6);
106
  display: flex;
107
  justify-content: space-between;
108
  align-items: center;
109
+ flex-wrap: wrap;
110
+ gap: var(--space-4);
111
+ max-width: none;
112
+ }
113
+
114
+ .header-brand {
115
+ display: flex;
116
+ align-items: center;
117
+ gap: var(--space-3);
118
  }
119
 
120
  .logo {
121
  font-size: 1.5rem;
122
+ font-weight: 700;
123
+ letter-spacing: -0.025em;
124
+ }
125
+
126
+ .header-stats {
127
  display: flex;
128
  align-items: center;
129
+ gap: var(--space-6);
130
+ }
131
+
132
+ .stat-item {
133
+ display: flex;
134
+ align-items: center;
135
+ gap: var(--space-2);
136
+ padding: var(--space-2) var(--space-4);
137
+ background: rgba(255, 255, 255, 0.1);
138
+ border-radius: var(--radius-lg);
139
+ border: 1px solid rgba(255, 255, 255, 0.2);
140
+ backdrop-filter: blur(8px);
141
+ }
142
+
143
+ .stat-icon {
144
+ font-size: 1.125rem;
145
+ }
146
+
147
+ .stat-text {
148
+ font-size: 0.875rem;
149
+ font-weight: 500;
150
+ }
151
+
152
+ .stat-number {
153
+ font-size: 1rem;
154
+ font-weight: 700;
155
+ color: var(--primary-100);
156
  }
157
 
158
  .header-actions {
159
  display: flex;
 
160
  align-items: center;
161
+ gap: var(--space-3);
162
+ }
163
+
164
+ .user-info {
165
+ display: flex;
166
+ align-items: center;
167
+ gap: var(--space-3);
168
+ padding: var(--space-2) var(--space-4);
169
+ background: rgba(255, 255, 255, 0.1);
170
+ border-radius: var(--radius-xl);
171
+ border: 1px solid rgba(255, 255, 255, 0.2);
172
+ backdrop-filter: blur(8px);
173
+ }
174
+
175
+ .user-avatar {
176
+ width: 32px;
177
+ height: 32px;
178
+ border-radius: 50%;
179
+ background: var(--primary-500);
180
+ display: flex;
181
+ align-items: center;
182
+ justify-content: center;
183
+ font-weight: 600;
184
+ font-size: 0.875rem;
185
+ }
186
+
187
+ .user-details {
188
+ display: flex;
189
+ flex-direction: column;
190
+ }
191
+
192
+ .user-name {
193
+ font-size: 0.875rem;
194
+ font-weight: 600;
195
+ }
196
+
197
+ .user-role {
198
+ font-size: 0.75rem;
199
+ opacity: 0.8;
200
  }
201
 
202
  .btn {
203
+ display: inline-flex;
204
+ align-items: center;
205
+ justify-content: center;
206
+ gap: var(--space-2);
207
+ padding: var(--space-2) var(--space-4);
208
+ font-size: 0.875rem;
209
+ font-weight: 500;
210
+ line-height: 1.25;
211
+ border-radius: var(--radius-md);
212
  border: none;
 
213
  cursor: pointer;
214
+ transition: all 0.15s ease;
 
 
215
  text-decoration: none;
216
+ outline: none;
217
+ white-space: nowrap;
 
218
  }
219
 
220
  .btn-primary {
221
+ background: var(--primary-500);
222
  color: white;
223
  }
224
 
225
  .btn-primary:hover {
226
+ background: var(--primary-600);
227
  transform: translateY(-1px);
228
+ box-shadow: var(--shadow-md);
229
  }
230
 
231
  .btn-secondary {
232
+ background: rgba(255, 255, 255, 0.1);
233
  color: white;
234
+ border: 1px solid rgba(255, 255, 255, 0.2);
235
  }
236
 
237
  .btn-secondary:hover {
238
+ background: rgba(255, 255, 255, 0.2);
239
+ transform: translateY(-1px);
240
+ }
241
+
242
+ .btn-sm {
243
+ padding: var(--space-1) var(--space-3);
244
+ font-size: 0.8125rem;
245
  }
246
 
247
  /* Map Container */
 
257
  z-index: 1;
258
  }
259
 
260
+ /* Controls Panel */
261
+ .controls-panel {
262
  position: absolute;
263
+ top: var(--space-4);
264
+ left: var(--space-4);
265
  z-index: 1000;
266
+ background: rgba(255, 255, 255, 0.95);
267
+ backdrop-filter: blur(12px);
268
+ border-radius: var(--radius-xl);
269
+ border: 1px solid var(--gray-200);
270
+ box-shadow: var(--shadow-lg);
271
+ overflow: hidden;
272
+ }
273
+
274
+ .controls-header {
275
+ padding: var(--space-4);
276
+ border-bottom: 1px solid var(--gray-100);
277
+ }
278
+
279
+ .controls-title {
280
+ font-size: 0.875rem;
281
+ font-weight: 600;
282
+ color: var(--gray-900);
283
+ margin: 0;
284
+ }
285
+
286
+ .controls-content {
287
+ padding: var(--space-3);
288
+ }
289
+
290
+ .control-group {
291
  display: flex;
292
  flex-direction: column;
293
+ gap: var(--space-2);
294
+ }
295
+
296
+ .control-button {
297
+ background: var(--gray-50);
298
+ color: var(--gray-700);
299
+ border: 1px solid var(--gray-200);
300
+ padding: var(--space-3) var(--space-4);
301
+ border-radius: var(--radius-md);
302
+ font-size: 0.875rem;
303
+ font-weight: 500;
304
+ cursor: pointer;
305
+ transition: all 0.15s ease;
306
+ display: flex;
307
+ align-items: center;
308
+ gap: var(--space-2);
309
  }
310
 
311
+ .control-button:hover {
312
+ background: var(--gray-100);
313
+ border-color: var(--gray-300);
314
+ transform: translateY(-1px);
315
+ box-shadow: var(--shadow-sm);
 
 
316
  }
317
 
318
+ .control-button.active {
319
+ background: var(--primary-50);
320
+ color: var(--primary-700);
321
+ border-color: var(--primary-500);
 
 
322
  }
323
 
324
+ /* Location Panel */
325
+ .location-panel {
326
+ position: absolute;
327
+ bottom: var(--space-4);
328
+ left: var(--space-4);
329
+ right: var(--space-4);
330
+ z-index: 1000;
331
+ background: rgba(255, 255, 255, 0.95);
332
+ backdrop-filter: blur(12px);
333
+ border-radius: var(--radius-xl);
334
+ border: 1px solid var(--gray-200);
335
+ box-shadow: var(--shadow-lg);
336
  transform: translateY(100%);
337
  transition: transform 0.3s ease;
338
  }
339
 
340
+ .location-panel.active {
341
  transform: translateY(0);
342
  }
343
 
344
+ .location-header {
345
+ padding: var(--space-4);
346
+ border-bottom: 1px solid var(--gray-100);
347
+ }
348
+
349
+ .location-title {
350
+ font-size: 1rem;
351
+ font-weight: 600;
352
+ color: var(--gray-900);
353
+ margin: 0;
354
+ }
355
+
356
+ .location-content {
357
+ padding: var(--space-4);
358
+ }
359
+
360
+ .coordinates-grid {
361
+ display: grid;
362
+ grid-template-columns: 1fr 1fr;
363
+ gap: var(--space-4);
364
+ margin-bottom: var(--space-4);
365
  }
366
 
367
  .coord-item {
 
368
  text-align: center;
369
  }
370
 
371
  .coord-label {
372
+ font-size: 0.75rem;
373
+ font-weight: 500;
374
+ color: var(--gray-600);
375
+ margin-bottom: var(--space-1);
376
+ text-transform: uppercase;
377
+ letter-spacing: 0.05em;
378
  }
379
 
380
  .coord-value {
381
+ font-size: 1.125rem;
382
+ font-weight: 700;
383
+ color: var(--primary-600);
384
+ font-variant-numeric: tabular-nums;
385
  }
386
 
387
+ .location-actions {
388
+ display: grid;
389
+ grid-template-columns: 1fr 1fr;
390
+ gap: var(--space-3);
391
  }
392
 
393
+ /* Messages */
394
+ .message {
395
+ position: absolute;
396
+ top: var(--space-4);
397
+ left: 50%;
398
+ transform: translateX(-50%);
399
+ padding: var(--space-3) var(--space-6);
400
+ border-radius: var(--radius-lg);
401
+ font-size: 0.875rem;
402
+ font-weight: 500;
403
+ z-index: 1500;
404
+ opacity: 0;
405
+ transition: opacity 0.3s ease;
406
+ pointer-events: none;
407
  }
408
 
409
+ .message.show {
410
+ opacity: 1;
 
 
 
 
 
 
 
 
411
  }
412
 
413
+ .message.success {
414
+ background: var(--green-50);
415
+ color: var(--green-600);
416
+ border: 1px solid var(--green-500);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
417
  }
418
 
419
+ .message.error {
420
+ background: var(--red-50);
421
+ color: var(--red-600);
422
+ border: 1px solid var(--red-500);
 
 
423
  }
424
 
425
+ .message.info {
426
+ background: var(--primary-50);
427
+ color: var(--primary-600);
428
+ border: 1px solid var(--primary-500);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
429
  }
430
 
431
+ /* Loading */
432
  .loading {
433
  position: absolute;
434
  top: 50%;
435
  left: 50%;
436
  transform: translate(-50%, -50%);
437
+ background: rgba(255, 255, 255, 0.95);
438
+ backdrop-filter: blur(8px);
439
+ padding: var(--space-6) var(--space-8);
440
+ border-radius: var(--radius-xl);
441
  z-index: 2000;
442
+ display: flex;
443
+ align-items: center;
444
+ gap: var(--space-3);
445
+ box-shadow: var(--shadow-lg);
446
+ border: 1px solid var(--gray-200);
447
  }
448
 
449
  .spinner {
450
+ border: 2px solid var(--gray-200);
451
+ border-top: 2px solid var(--primary-500);
452
  border-radius: 50%;
453
+ width: 1.5rem;
454
+ height: 1.5rem;
455
  animation: spin 1s linear infinite;
 
456
  }
457
 
458
  @keyframes spin {
 
460
  100% { transform: rotate(360deg); }
461
  }
462
 
463
+ .loading-text {
464
+ color: var(--gray-700);
465
+ font-size: 0.875rem;
466
+ font-weight: 500;
 
 
 
 
 
 
 
 
467
  }
468
 
469
+ /* Custom Leaflet Overrides */
470
+ .leaflet-control-container {
471
+ display: none;
472
  }
473
 
474
+ .leaflet-popup-content-wrapper {
475
+ background: white;
476
+ border-radius: var(--radius-lg);
477
+ box-shadow: var(--shadow-xl);
478
+ border: 1px solid var(--gray-200);
479
  }
480
 
481
+ .leaflet-popup-tip {
482
+ background: white;
483
+ border: 1px solid var(--gray-200);
484
  }
485
 
486
+ .leaflet-popup-content {
487
+ margin: 0;
488
+ line-height: 1.5;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
489
  }
490
 
491
+ /* Tree Popup Content */
492
+ .tree-popup {
493
+ padding: var(--space-4);
494
+ min-width: 280px;
495
  }
496
 
497
+ .tree-popup-header {
 
498
  display: flex;
 
499
  align-items: center;
500
+ gap: var(--space-3);
501
+ margin-bottom: var(--space-3);
502
+ padding-bottom: var(--space-3);
503
+ border-bottom: 1px solid var(--gray-100);
504
  }
505
 
506
+ .tree-icon {
507
+ font-size: 2rem;
508
+ color: var(--green-500);
 
 
 
 
 
 
 
509
  }
510
 
511
+ .tree-popup-title {
512
+ flex: 1;
 
 
 
 
513
  }
514
 
515
+ .tree-name {
516
+ font-size: 1rem;
517
+ font-weight: 600;
518
+ color: var(--gray-900);
519
+ margin: 0;
 
 
 
520
  }
521
 
522
+ .tree-scientific {
523
+ font-size: 0.75rem;
524
+ color: var(--gray-600);
525
+ font-style: italic;
526
+ margin-top: var(--space-1);
527
  }
528
 
529
+ .tree-popup-details {
530
+ display: grid;
531
+ gap: var(--space-2);
532
+ margin-bottom: var(--space-3);
 
 
 
 
 
 
 
533
  }
534
 
535
+ .detail-row {
536
+ display: flex;
537
+ justify-content: space-between;
538
+ align-items: center;
539
+ font-size: 0.8125rem;
540
  }
541
 
542
+ .detail-label {
543
+ color: var(--gray-600);
544
+ font-weight: 500;
545
  }
546
 
547
+ .detail-value {
548
+ color: var(--gray-900);
549
  font-weight: 600;
 
 
 
550
  }
551
 
552
+ .tree-popup-actions {
553
+ display: grid;
554
+ grid-template-columns: 1fr 1fr auto;
555
+ gap: var(--space-2);
556
+ }
557
+
558
+ .btn-icon {
559
+ background: transparent;
560
+ border: 1px solid var(--gray-300);
561
+ color: var(--gray-600);
562
+ padding: var(--space-2);
563
+ border-radius: var(--radius-md);
564
+ cursor: pointer;
565
+ transition: all 0.15s ease;
566
+ display: flex;
567
+ align-items: center;
568
+ justify-content: center;
569
  }
570
 
571
+ .btn-icon:hover {
572
+ background: var(--gray-50);
573
+ border-color: var(--gray-400);
574
  }
575
 
576
+ .btn-icon.edit:hover {
577
+ background: var(--primary-50);
578
+ border-color: var(--primary-500);
579
+ color: var(--primary-600);
 
580
  }
581
 
582
+ .btn-icon.delete:hover {
583
+ background: var(--red-50);
584
+ border-color: var(--red-500);
585
+ color: var(--red-600);
586
+ }
587
+
588
+ /* Mobile Optimizations */
589
+ @media (max-width: 768px) {
590
+ .header-content {
591
+ padding: var(--space-3) var(--space-4);
592
+ }
593
+
594
+ .header-stats {
595
+ gap: var(--space-3);
596
+ }
597
+
598
+ .stat-item {
599
+ padding: var(--space-1) var(--space-3);
600
+ }
601
+
602
+ .stat-text {
603
+ display: none;
604
+ }
605
+
606
+ .logo {
607
+ font-size: 1.25rem;
608
+ }
609
+
610
+ .controls-panel {
611
+ top: var(--space-3);
612
+ left: var(--space-3);
613
+ }
614
+
615
+ .location-panel {
616
+ bottom: var(--space-3);
617
+ left: var(--space-3);
618
+ right: var(--space-3);
619
+ }
620
+
621
+ .coordinates-grid {
622
+ grid-template-columns: 1fr;
623
+ gap: var(--space-3);
624
+ }
625
+
626
+ .location-actions {
627
+ grid-template-columns: 1fr;
628
+ }
629
+
630
+ .user-details {
631
+ display: none;
632
+ }
633
+
634
+ .tree-popup {
635
+ min-width: 260px;
636
+ padding: var(--space-3);
637
+ }
638
+
639
+ .tree-popup-actions {
640
+ grid-template-columns: 1fr;
641
+ gap: var(--space-2);
642
+ }
643
+ }
644
+
645
+ /* Gesture Hint */
646
+ .gesture-hint {
647
+ position: absolute;
648
+ bottom: var(--space-16);
649
+ left: 50%;
650
+ transform: translateX(-50%);
651
+ background: rgba(0, 0, 0, 0.8);
652
+ color: white;
653
+ padding: var(--space-3) var(--space-6);
654
+ border-radius: var(--radius-xl);
655
+ font-size: 0.875rem;
656
+ font-weight: 500;
657
+ z-index: 1000;
658
+ animation: fadeInOut 4s ease-in-out;
659
+ pointer-events: none;
660
+ }
661
+
662
+ @keyframes fadeInOut {
663
+ 0%, 100% { opacity: 0; }
664
+ 25%, 75% { opacity: 1; }
665
+ }
666
+
667
+ /* Focus States */
668
+ .btn:focus-visible,
669
+ .control-button:focus-visible,
670
+ .btn-icon:focus-visible {
671
+ outline: 2px solid var(--primary-500);
672
+ outline-offset: 2px;
673
  }
674
  </style>
675
  <script>
676
  // Force refresh if we detect cached version
677
  (function() {
678
+ const currentVersion = '4.0.0';
679
  const lastVersion = sessionStorage.getItem('treetrack_version');
680
+ if (!lastVersion || lastVersion !== currentVersion) {
 
 
 
681
  sessionStorage.setItem('treetrack_version', currentVersion);
682
+ if (lastVersion && lastVersion !== currentVersion) {
683
+ location.reload(true);
684
+ return;
685
+ }
686
  }
 
 
 
 
 
 
 
 
687
  })();
688
  </script>
689
  </head>
 
691
  <div class="app-container">
692
  <!-- Header -->
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>
710
+ </div>
711
+
712
+ <div class="header-actions">
713
+ <div class="user-info" id="userInfo">
714
+ <div class="user-avatar" id="userAvatar">U</div>
715
+ <div class="user-details">
716
+ <div class="user-name" id="userName">Loading...</div>
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>
724
  </div>
725
 
 
727
  <div class="map-container">
728
  <div id="map"></div>
729
 
730
+ <!-- Controls Panel -->
731
+ <div class="controls-panel">
732
+ <div class="controls-header">
733
+ <h3 class="controls-title">Map Controls</h3>
734
+ </div>
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>
750
  </div>
751
  </div>
752
 
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">
760
  <div class="coord-item">
761
  <div class="coord-label">Latitude</div>
762
  <div class="coord-value" id="latValue">--</div>
 
766
  <div class="coord-value" id="lngValue">--</div>
767
  </div>
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>
778
  </div>
 
780
  <!-- Loading -->
781
  <div id="loading" class="loading" style="display: none;">
782
  <div class="spinner"></div>
783
+ <div class="loading-text">Loading map...</div>
784
  </div>
785
 
786
  <!-- Messages -->
 
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>
795
 
796
  <!-- Leaflet JS -->
797
  <script src="https://unpkg.com/[email protected]/dist/leaflet.js"></script>
798
+ <script src="/static/map_enhanced.js?v=4.0.0&t=1754657582"></script>
799
  </body>
800
  </html>
static/map_old.html ADDED
@@ -0,0 +1,545 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate">
7
+ <meta http-equiv="Pragma" content="no-cache">
8
+ <meta http-equiv="Expires" content="0">
9
+ <title>TreeTrack Map - Interactive Field View</title>
10
+
11
+ <!-- Leaflet CSS -->
12
+ <link rel="stylesheet" href="https://unpkg.com/[email protected]/dist/leaflet.css" />
13
+
14
+ <style>
15
+ * {
16
+ margin: 0;
17
+ padding: 0;
18
+ box-sizing: border-box;
19
+ }
20
+
21
+ body {
22
+ font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
23
+ background: #1a1a1a;
24
+ color: white;
25
+ height: 100vh;
26
+ overflow: hidden;
27
+ }
28
+
29
+ .app-container {
30
+ height: 100vh;
31
+ display: flex;
32
+ flex-direction: column;
33
+ }
34
+
35
+ /* Header */
36
+ .header {
37
+ background: linear-gradient(135deg, #1e40af 0%, #3b82f6 100%) !important;
38
+ padding: 1rem 1.5rem;
39
+ display: flex;
40
+ justify-content: space-between;
41
+ align-items: center;
42
+ box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1);
43
+ z-index: 1000;
44
+ /* Red border removed after debugging */
45
+ }
46
+
47
+ .logo {
48
+ font-size: 1.5rem;
49
+ font-weight: 600;
50
+ display: flex;
51
+ align-items: center;
52
+ gap: 10px;
53
+ letter-spacing: -0.025em;
54
+ }
55
+
56
+ .header-actions {
57
+ display: flex;
58
+ gap: 10px;
59
+ align-items: center;
60
+ }
61
+
62
+ .btn {
63
+ padding: 8px 15px;
64
+ border: none;
65
+ border-radius: 20px;
66
+ cursor: pointer;
67
+ font-size: 14px;
68
+ font-weight: 600;
69
+ transition: all 0.3s ease;
70
+ text-decoration: none;
71
+ display: inline-flex;
72
+ align-items: center;
73
+ gap: 5px;
74
+ }
75
+
76
+ .btn-primary {
77
+ background: #3b82f6;
78
+ color: white;
79
+ }
80
+
81
+ .btn-primary:hover {
82
+ background: #2563eb;
83
+ transform: translateY(-1px);
84
+ }
85
+
86
+ .btn-secondary {
87
+ background: rgba(255,255,255,0.2);
88
+ color: white;
89
+ backdrop-filter: blur(10px);
90
+ }
91
+
92
+ .btn-secondary:hover {
93
+ background: rgba(255,255,255,0.3);
94
+ }
95
+
96
+ /* Map Container */
97
+ .map-container {
98
+ flex: 1;
99
+ position: relative;
100
+ overflow: hidden;
101
+ }
102
+
103
+ #map {
104
+ width: 100%;
105
+ height: 100%;
106
+ z-index: 1;
107
+ }
108
+
109
+ /* Floating Controls */
110
+ .floating-controls {
111
+ position: absolute;
112
+ top: 20px;
113
+ right: 20px;
114
+ z-index: 1000;
115
+ display: flex;
116
+ flex-direction: column;
117
+ gap: 10px;
118
+ }
119
+
120
+ .control-panel {
121
+ background: rgba(0,0,0,0.8);
122
+ backdrop-filter: blur(10px);
123
+ border-radius: 15px;
124
+ padding: 15px;
125
+ box-shadow: 0 8px 32px rgba(0,0,0,0.3);
126
+ border: 1px solid rgba(255,255,255,0.1);
127
+ }
128
+
129
+ .location-info {
130
+ position: absolute;
131
+ bottom: 20px;
132
+ left: 20px;
133
+ right: 20px;
134
+ z-index: 1000;
135
+ }
136
+
137
+ .info-panel {
138
+ background: rgba(0,0,0,0.9);
139
+ backdrop-filter: blur(15px);
140
+ border-radius: 15px;
141
+ padding: 20px;
142
+ box-shadow: 0 8px 32px rgba(0,0,0,0.5);
143
+ border: 1px solid rgba(255,255,255,0.1);
144
+ transform: translateY(100%);
145
+ transition: transform 0.3s ease;
146
+ }
147
+
148
+ .info-panel.active {
149
+ transform: translateY(0);
150
+ }
151
+
152
+ .coordinates {
153
+ display: flex;
154
+ gap: 20px;
155
+ margin-bottom: 15px;
156
+ }
157
+
158
+ .coord-item {
159
+ flex: 1;
160
+ text-align: center;
161
+ }
162
+
163
+ .coord-label {
164
+ font-size: 12px;
165
+ opacity: 0.7;
166
+ margin-bottom: 5px;
167
+ }
168
+
169
+ .coord-value {
170
+ font-size: 18px;
171
+ font-weight: bold;
172
+ color: #3b82f6;
173
+ }
174
+
175
+ .quick-actions {
176
+ display: flex;
177
+ gap: 10px;
178
+ margin-top: 15px;
179
+ }
180
+
181
+ .quick-actions .btn {
182
+ flex: 1;
183
+ }
184
+
185
+ /* Tree Markers Info */
186
+ .tree-counter {
187
+ background: rgba(59, 130, 246, 0.9);
188
+ color: white;
189
+ padding: 10px 15px;
190
+ border-radius: 20px;
191
+ font-weight: bold;
192
+ display: flex;
193
+ align-items: center;
194
+ gap: 8px;
195
+ }
196
+
197
+ /* Mobile Optimizations */
198
+ @media (max-width: 768px) {
199
+ .header {
200
+ padding: 10px 15px;
201
+ }
202
+
203
+ .logo {
204
+ font-size: 1.3rem;
205
+ }
206
+
207
+ .floating-controls {
208
+ top: 10px;
209
+ right: 10px;
210
+ }
211
+
212
+ .control-panel {
213
+ padding: 10px;
214
+ }
215
+
216
+ .location-info {
217
+ bottom: 10px;
218
+ left: 10px;
219
+ right: 10px;
220
+ }
221
+
222
+ .coordinates {
223
+ flex-direction: column;
224
+ gap: 10px;
225
+ }
226
+
227
+ .quick-actions {
228
+ flex-direction: column;
229
+ }
230
+
231
+ .btn {
232
+ padding: 12px 20px;
233
+ font-size: 16px;
234
+ }
235
+ }
236
+
237
+ /* Custom Pin Styles */
238
+ .tree-pin {
239
+ background: #3b82f6;
240
+ border: 3px solid white;
241
+ border-radius: 50%;
242
+ box-shadow: 0 2px 10px rgba(0,0,0,0.3);
243
+ }
244
+
245
+ .temp-pin {
246
+ background: #ff6b35;
247
+ border: 3px solid white;
248
+ border-radius: 50%;
249
+ box-shadow:
250
+ 0 2px 10px rgba(0,0,0,0.3),
251
+ 0 0 15px rgba(255, 107, 53, 0.6),
252
+ 0 0 25px rgba(255, 107, 53, 0.3);
253
+ animation: gentle-glow 3s ease-in-out infinite;
254
+ }
255
+
256
+ @keyframes gentle-glow {
257
+ 0% {
258
+ box-shadow:
259
+ 0 2px 10px rgba(0,0,0,0.3),
260
+ 0 0 15px rgba(255, 107, 53, 0.6),
261
+ 0 0 25px rgba(255, 107, 53, 0.3);
262
+ }
263
+ 50% {
264
+ box-shadow:
265
+ 0 2px 12px rgba(0,0,0,0.4),
266
+ 0 0 20px rgba(255, 107, 53, 0.8),
267
+ 0 0 35px rgba(255, 107, 53, 0.5);
268
+ }
269
+ 100% {
270
+ box-shadow:
271
+ 0 2px 10px rgba(0,0,0,0.3),
272
+ 0 0 15px rgba(255, 107, 53, 0.6),
273
+ 0 0 25px rgba(255, 107, 53, 0.3);
274
+ }
275
+ }
276
+
277
+ /* Loading States */
278
+ .loading {
279
+ position: absolute;
280
+ top: 50%;
281
+ left: 50%;
282
+ transform: translate(-50%, -50%);
283
+ background: rgba(0,0,0,0.8);
284
+ padding: 20px 30px;
285
+ border-radius: 10px;
286
+ z-index: 2000;
287
+ }
288
+
289
+ .spinner {
290
+ border: 3px solid rgba(255,255,255,0.3);
291
+ border-top: 3px solid #3b82f6;
292
+ border-radius: 50%;
293
+ width: 30px;
294
+ height: 30px;
295
+ animation: spin 1s linear infinite;
296
+ margin: 0 auto 10px;
297
+ }
298
+
299
+ @keyframes spin {
300
+ 0% { transform: rotate(0deg); }
301
+ 100% { transform: rotate(360deg); }
302
+ }
303
+
304
+ /* Success/Error Messages */
305
+ .message {
306
+ position: absolute;
307
+ top: 80px;
308
+ left: 50%;
309
+ transform: translateX(-50%);
310
+ padding: 15px 25px;
311
+ border-radius: 25px;
312
+ font-weight: 600;
313
+ z-index: 1500;
314
+ opacity: 0;
315
+ transition: opacity 0.3s ease;
316
+ }
317
+
318
+ .message.show {
319
+ opacity: 1;
320
+ }
321
+
322
+ .message.success {
323
+ background: linear-gradient(45deg, #3b82f6, #2563eb);
324
+ color: white;
325
+ }
326
+
327
+ .message.error {
328
+ background: linear-gradient(45deg, #f44336, #d32f2f);
329
+ color: white;
330
+ }
331
+
332
+ /* Gesture Instructions */
333
+ .gesture-hint {
334
+ position: absolute;
335
+ bottom: 120px;
336
+ left: 50%;
337
+ transform: translateX(-50%);
338
+ background: rgba(0,0,0,0.7);
339
+ color: white;
340
+ padding: 10px 20px;
341
+ border-radius: 20px;
342
+ font-size: 14px;
343
+ z-index: 1000;
344
+ animation: fadeInOut 4s ease-in-out;
345
+ }
346
+
347
+ @keyframes fadeInOut {
348
+ 0%, 100% { opacity: 0; }
349
+ 20%, 80% { opacity: 1; }
350
+ }
351
+
352
+ /* Custom Tree Marker Styles */
353
+ .custom-tree-icon {
354
+ background: transparent !important;
355
+ border: none !important;
356
+ }
357
+
358
+ .custom-tree-marker {
359
+ position: relative;
360
+ display: flex;
361
+ flex-direction: column;
362
+ align-items: center;
363
+ }
364
+
365
+ .tree-icon-container {
366
+ background: linear-gradient(145deg, #ffffff, #f0f0f0);
367
+ border-radius: 50%;
368
+ padding: 4px;
369
+ box-shadow:
370
+ 0 4px 8px rgba(0,0,0,0.15),
371
+ 0 2px 4px rgba(0,0,0,0.1),
372
+ inset 0 1px 0 rgba(255,255,255,0.2);
373
+ transition: all 0.3s ease;
374
+ cursor: pointer;
375
+ }
376
+
377
+ .tree-icon-container:hover {
378
+ transform: translateY(-2px) scale(1.1);
379
+ box-shadow:
380
+ 0 6px 16px rgba(0,0,0,0.2),
381
+ 0 4px 8px rgba(0,0,0,0.15),
382
+ inset 0 1px 0 rgba(255,255,255,0.3);
383
+ }
384
+
385
+ .tree-marker-shadow {
386
+ width: 12px;
387
+ height: 6px;
388
+ background: rgba(0,0,0,0.3);
389
+ border-radius: 50%;
390
+ margin-top: 2px;
391
+ filter: blur(1px);
392
+ transition: all 0.3s ease;
393
+ }
394
+
395
+ .custom-tree-marker:hover .tree-marker-shadow {
396
+ width: 16px;
397
+ background: rgba(0,0,0,0.4);
398
+ }
399
+
400
+ /* Tooltip Styles */
401
+ .leaflet-tooltip.tree-tooltip {
402
+ background: linear-gradient(145deg, #1e40af, #1d4ed8) !important;
403
+ border: 1px solid rgba(255,255,255,0.2) !important;
404
+ border-radius: 8px !important;
405
+ box-shadow: 0 4px 12px rgba(0,0,0,0.3) !important;
406
+ color: white !important;
407
+ font-family: 'Segoe UI', sans-serif !important;
408
+ font-size: 13px !important;
409
+ padding: 8px 12px !important;
410
+ backdrop-filter: blur(10px);
411
+ }
412
+
413
+ .leaflet-tooltip.tree-tooltip::before {
414
+ border-top-color: #1e40af !important;
415
+ }
416
+
417
+ .tree-tooltip-content {
418
+ min-width: 80px;
419
+ text-align: center;
420
+ }
421
+
422
+ .tree-name {
423
+ font-weight: 600;
424
+ font-size: 14px;
425
+ margin-bottom: 2px;
426
+ color: #ffffff;
427
+ }
428
+
429
+ .tree-details {
430
+ font-size: 11px;
431
+ opacity: 0.9;
432
+ color: #e8f5e8;
433
+ }
434
+
435
+ /* Enhanced Popup Styles */
436
+ .leaflet-popup.tree-popup {
437
+ margin-bottom: 10px;
438
+ }
439
+
440
+ .leaflet-popup.tree-popup .leaflet-popup-content-wrapper {
441
+ background: linear-gradient(145deg, #ffffff, #f8f9fa);
442
+ border-radius: 12px;
443
+ box-shadow: 0 8px 24px rgba(0,0,0,0.15);
444
+ border: 1px solid rgba(44, 85, 48, 0.1);
445
+ }
446
+
447
+ .leaflet-popup.tree-popup .leaflet-popup-tip {
448
+ background: #ffffff;
449
+ border: 1px solid rgba(59, 130, 246, 0.1);
450
+ box-shadow: 0 2px 4px rgba(0,0,0,0.1);
451
+ }
452
+ </style>
453
+ <script>
454
+ // Force refresh if we detect cached version
455
+ (function() {
456
+ const currentVersion = '3.1754653904';
457
+ const lastVersion = sessionStorage.getItem('treetrack_version');
458
+
459
+ // Always reload on version mismatch
460
+ if (lastVersion && lastVersion !== currentVersion) {
461
+ console.log('Version changed from', lastVersion, 'to', currentVersion, '- forcing reload');
462
+ sessionStorage.setItem('treetrack_version', currentVersion);
463
+ location.reload(true);
464
+ return;
465
+ }
466
+
467
+ // Set version if not set
468
+ if (!lastVersion) {
469
+ sessionStorage.setItem('treetrack_version', currentVersion);
470
+ }
471
+
472
+ // Add debug info to page title
473
+ document.title = 'TreeTrack Map - ' + new Date().toLocaleTimeString();
474
+ })();
475
+ </script>
476
+ </head>
477
+ <body>
478
+ <div class="app-container">
479
+ <!-- Header -->
480
+ <div class="header">
481
+ <div class="logo">
482
+ πŸ”₯ TreeTrack Map V4.0 πŸ”₯
483
+ </div>
484
+ <div class="header-actions">
485
+ <div class="tree-counter">
486
+ <span>Trees:</span>
487
+ <span id="treeCount">0</span>
488
+ </div>
489
+ <a href="/static/index.html" class="btn btn-secondary">Add Tree</a>
490
+ </div>
491
+ </div>
492
+
493
+ <!-- Map Container -->
494
+ <div class="map-container">
495
+ <div id="map"></div>
496
+
497
+ <!-- Floating Controls -->
498
+ <div class="floating-controls">
499
+ <div class="control-panel">
500
+ <button id="myLocationBtn" class="btn btn-primary">My Location</button>
501
+ <button id="clearPinsBtn" class="btn btn-secondary" style="margin-top: 8px;">Clear Pins</button>
502
+ </div>
503
+ </div>
504
+
505
+ <!-- Location Info Panel -->
506
+ <div class="location-info">
507
+ <div class="info-panel" id="infoPanel">
508
+ <div class="coordinates">
509
+ <div class="coord-item">
510
+ <div class="coord-label">Latitude</div>
511
+ <div class="coord-value" id="latValue">--</div>
512
+ </div>
513
+ <div class="coord-item">
514
+ <div class="coord-label">Longitude</div>
515
+ <div class="coord-value" id="lngValue">--</div>
516
+ </div>
517
+ </div>
518
+ <div class="quick-actions">
519
+ <button id="useLocationBtn" class="btn btn-primary">Use This Location</button>
520
+ <button id="cancelBtn" class="btn btn-secondary">Cancel</button>
521
+ </div>
522
+ </div>
523
+ </div>
524
+
525
+ <!-- Loading -->
526
+ <div id="loading" class="loading" style="display: none;">
527
+ <div class="spinner"></div>
528
+ <div>Loading map...</div>
529
+ </div>
530
+
531
+ <!-- Messages -->
532
+ <div id="message" class="message"></div>
533
+
534
+ <!-- Gesture Hint -->
535
+ <div class="gesture-hint">
536
+ Tap anywhere on map to drop a pin
537
+ </div>
538
+ </div>
539
+ </div>
540
+
541
+ <!-- Leaflet JS -->
542
+ <script src="https://unpkg.com/[email protected]/dist/leaflet.js"></script>
543
+ <script src="/static/map.js?v=3.1754653904&t=1754653904"></script>
544
+ </body>
545
+ </html>