Spaces:
Runtime error
Runtime error
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate"> | |
| <meta http-equiv="Pragma" content="no-cache"> | |
| <meta http-equiv="Expires" content="0"> | |
| <title>TreeTrack - Professional Field Research</title> | |
| <link rel="preconnect" href="https://unpkg.com" crossorigin> | |
| <link rel="dns-prefetch" href="//unpkg.com"> | |
| <link rel="prefetch" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css"> | |
| <link rel="icon" type="image/png" href="/static/image/icons8-tree-96.png"> | |
| <link rel="apple-touch-icon" href="/static/image/icons8-tree-96.png"> | |
| <link rel="stylesheet" href="/static/css/design-system.css"> | |
| <style> | |
| :root { | |
| /* Remove color overrides - use design system's green nature colors */ | |
| --gray-50: #f8fafc; | |
| --gray-100: #f1f5f9; | |
| --gray-200: #e2e8f0; | |
| --gray-300: #cbd5e1; | |
| --gray-400: #94a3b8; | |
| --gray-500: #64748b; | |
| --gray-600: #475569; | |
| --gray-700: #334155; | |
| --gray-800: #1e293b; | |
| --gray-900: #0f172a; | |
| --green-50: #f0fdf4; | |
| --green-500: #22c55e; | |
| --green-600: #16a34a; | |
| --red-50: #fef2f2; | |
| --red-500: #ef4444; | |
| --red-600: #dc2626; | |
| /* Spacing */ | |
| --space-1: 0.25rem; | |
| --space-2: 0.5rem; | |
| --space-3: 0.75rem; | |
| --space-4: 1rem; | |
| --space-5: 1.25rem; | |
| --space-6: 1.5rem; | |
| --space-8: 2rem; | |
| --space-12: 3rem; | |
| --space-16: 4rem; | |
| /* Radius */ | |
| --radius-sm: 0.375rem; | |
| --radius-md: 0.5rem; | |
| --radius-lg: 0.75rem; | |
| --radius-xl: 1rem; | |
| --radius-2xl: 1.5rem; | |
| /* Shadows */ | |
| --shadow-sm: 0 1px 2px 0 rgb(0 0 0 / 0.05); | |
| --shadow-md: 0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1); | |
| --shadow-lg: 0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1); | |
| --shadow-xl: 0 20px 25px -5px rgb(0 0 0 / 0.1), 0 8px 10px -6px rgb(0 0 0 / 0.1); | |
| } | |
| * { | |
| margin: 0; | |
| padding: 0; | |
| box-sizing: border-box; | |
| } | |
| body { | |
| font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif; | |
| font-feature-settings: 'cv11' 1, 'ss01' 1; | |
| line-height: 1.6; | |
| color: var(--gray-800); | |
| background: linear-gradient(135deg, #fefefe 0%, #f6f8f4 25%, #edf2e8 100%); | |
| min-height: 100vh; | |
| -webkit-font-smoothing: antialiased; | |
| -moz-osx-font-smoothing: grayscale; | |
| } | |
| /* Updated header with Granim animated background */ | |
| .tt-header { | |
| position: relative; | |
| overflow: hidden; | |
| } | |
| /* Ensure header content is above background */ | |
| .tt-header-content { | |
| position: relative; | |
| z-index: 2; | |
| } | |
| /* Button styles now handled by design system */ | |
| /* Main Content */ | |
| .main-container { | |
| max-width: 1400px; | |
| margin: 0 auto; | |
| padding: var(--space-8) var(--space-6); | |
| } | |
| .content-grid { | |
| display: grid; | |
| grid-template-columns: 1fr; | |
| gap: var(--space-8); | |
| } | |
| @media (min-width: 1200px) { | |
| .content-grid { | |
| grid-template-columns: 2fr 1fr; | |
| } | |
| } | |
| /* Cards */ | |
| .card { | |
| background: white; | |
| border-radius: var(--radius-xl); | |
| border: 1px solid var(--gray-200); | |
| box-shadow: var(--shadow-sm); | |
| transition: all 0.2s ease; | |
| overflow: hidden; | |
| } | |
| .card:hover { | |
| box-shadow: var(--shadow-md); | |
| border-color: var(--gray-300); | |
| } | |
| .card-header { | |
| padding: var(--space-6); | |
| border-bottom: 1px solid var(--gray-100); | |
| background: var(--gray-50); | |
| } | |
| .card-title { | |
| font-size: 1.25rem; | |
| font-weight: 600; | |
| color: var(--gray-900); | |
| margin: 0; | |
| } | |
| .card-subtitle { | |
| font-size: 0.875rem; | |
| color: var(--gray-600); | |
| margin-top: var(--space-1); | |
| } | |
| .card-content { | |
| padding: var(--space-6); | |
| } | |
| /* Form Sections */ | |
| .form-section { | |
| margin-bottom: var(--space-8); | |
| } | |
| .form-section:last-child { | |
| margin-bottom: 0; | |
| } | |
| .section-header { | |
| margin-bottom: var(--space-6); | |
| } | |
| .section-title { | |
| font-size: 1.125rem; | |
| font-weight: 600; | |
| color: var(--gray-900); | |
| margin: 0 0 var(--space-2) 0; | |
| } | |
| .section-description { | |
| font-size: 0.875rem; | |
| color: var(--gray-600); | |
| margin: 0; | |
| } | |
| /* Form Elements */ | |
| .form-group { | |
| margin-bottom: var(--space-5); | |
| } | |
| .form-row { | |
| display: grid; | |
| grid-template-columns: 1fr; | |
| gap: var(--space-4); | |
| } | |
| @media (min-width: 768px) { | |
| .form-row { | |
| grid-template-columns: 1fr 1fr; | |
| } | |
| } | |
| .form-label { | |
| display: block; | |
| font-size: 0.875rem; | |
| font-weight: 500; | |
| color: var(--gray-700); | |
| margin-bottom: var(--space-2); | |
| } | |
| .form-label.required::after { | |
| content: '*'; | |
| color: var(--red-500); | |
| margin-left: var(--space-1); | |
| } | |
| .form-input { | |
| width: 100%; | |
| padding: var(--space-3) var(--space-4); | |
| font-size: 0.875rem; | |
| line-height: 1.25; | |
| color: var(--gray-900); | |
| background: white; | |
| border: 1px solid var(--gray-300); | |
| border-radius: var(--radius-md); | |
| transition: all 0.15s ease; | |
| outline: none; | |
| } | |
| .form-input:focus { | |
| border-color: var(--primary-500); | |
| box-shadow: 0 0 0 3px rgb(59 130 246 / 0.1); | |
| } | |
| .form-input:disabled { | |
| background: var(--gray-50); | |
| color: var(--gray-500); | |
| cursor: not-allowed; | |
| } | |
| .form-textarea { | |
| resize: vertical; | |
| min-height: 100px; | |
| } | |
| .form-select { | |
| 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"); | |
| background-position: right 0.5rem center; | |
| background-repeat: no-repeat; | |
| background-size: 1.5em 1.5em; | |
| padding-right: 2.5rem; | |
| appearance: none; | |
| } | |
| /* Enhanced Multi-select with better UX */ | |
| .multi-select { | |
| border: 2px solid var(--gray-200); | |
| border-radius: var(--radius-lg); | |
| background: var(--gray-50); | |
| max-height: 200px; | |
| overflow-y: auto; | |
| overflow-x: hidden; | |
| box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.06); | |
| } | |
| /* Custom scrollbar for multi-select */ | |
| .multi-select::-webkit-scrollbar { | |
| width: 8px; | |
| } | |
| .multi-select::-webkit-scrollbar-track { | |
| background: var(--gray-100); | |
| border-radius: 4px; | |
| margin: 4px; | |
| } | |
| .multi-select::-webkit-scrollbar-thumb { | |
| background: var(--gray-400); | |
| border-radius: 4px; | |
| } | |
| .multi-select::-webkit-scrollbar-thumb:hover { | |
| background: var(--primary-500); | |
| } | |
| .multi-select label { | |
| display: flex; | |
| align-items: center; | |
| padding: var(--space-3) var(--space-4); | |
| margin: var(--space-2); | |
| border: 1px solid var(--gray-200); | |
| border-radius: var(--radius-md); | |
| background: white; | |
| cursor: pointer; | |
| transition: all 0.2s ease; | |
| box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05); | |
| font-size: 0.875rem; | |
| font-weight: 500; | |
| color: var(--gray-700); | |
| } | |
| .multi-select label:hover { | |
| border-color: var(--primary-300); | |
| background: var(--primary-50); | |
| transform: translateY(-1px); | |
| box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); | |
| } | |
| .multi-select label:has(input:checked) { | |
| border-color: var(--primary-500); | |
| background: var(--primary-100); | |
| color: var(--primary-700); | |
| font-weight: 600; | |
| } | |
| .multi-select input[type="checkbox"] { | |
| width: 1.125rem; | |
| height: 1.125rem; | |
| margin-right: var(--space-3); | |
| accent-color: var(--primary-500); | |
| cursor: pointer; | |
| } | |
| /* File Upload */ | |
| .file-upload-area { | |
| border: 2px dashed var(--gray-300); | |
| border-radius: var(--radius-lg); | |
| padding: var(--space-8); | |
| text-align: center; | |
| background: var(--gray-50); | |
| transition: all 0.2s ease; | |
| cursor: pointer; | |
| } | |
| .file-upload-area:hover { | |
| border-color: var(--primary-500); | |
| background: var(--primary-50); | |
| } | |
| .file-upload-area.dragover { | |
| border-color: var(--primary-500); | |
| background: var(--primary-50); | |
| } | |
| .file-upload-icon { | |
| font-size: 2rem; | |
| margin-bottom: var(--space-2); | |
| } | |
| .file-upload-text { | |
| font-size: 0.875rem; | |
| color: var(--gray-600); | |
| } | |
| .file-upload-hint { | |
| font-size: 0.75rem; | |
| color: var(--gray-500); | |
| margin-top: var(--space-1); | |
| } | |
| /* Location Controls */ | |
| .location-group { | |
| display: flex; | |
| align-items: end; | |
| gap: var(--space-3); | |
| } | |
| .location-input { | |
| flex: 1; | |
| } | |
| .location-button { | |
| flex-shrink: 0; | |
| } | |
| /* Enhanced location button styling with design system */ | |
| .location-buttons { | |
| display: flex; | |
| gap: var(--space-3); | |
| align-items: center; | |
| margin-top: var(--space-4); | |
| } | |
| .location-btn-gps { | |
| flex: 0 0 auto; | |
| } | |
| .location-btn-map { | |
| flex: 1; | |
| justify-content: center; | |
| } | |
| @media (max-width: 768px) { | |
| .location-buttons { | |
| flex-direction: column; | |
| gap: var(--space-4); | |
| margin-top: var(--space-6); | |
| } | |
| } | |
| @media (max-width: 480px) { | |
| .location-buttons { | |
| gap: var(--space-3); | |
| } | |
| } | |
| /* Form Actions */ | |
| .form-actions { | |
| display: flex; | |
| justify-content: flex-end; | |
| gap: var(--space-3); | |
| padding-top: var(--space-6); | |
| border-top: 1px solid var(--gray-200); | |
| margin-top: var(--space-8); | |
| } | |
| @media (max-width: 640px) { | |
| .form-actions { | |
| flex-direction: column; | |
| } | |
| .btn { | |
| width: 100%; | |
| justify-content: center; | |
| } | |
| } | |
| /* Messages */ | |
| .message { | |
| padding: var(--space-4); | |
| border-radius: var(--radius-md); | |
| margin-bottom: var(--space-6); | |
| font-size: 0.875rem; | |
| font-weight: 500; | |
| border: 1px solid transparent; | |
| } | |
| .message.success { | |
| background: var(--green-50); | |
| color: var(--green-600); | |
| border-color: var(--green-500); | |
| } | |
| .message.error { | |
| background: var(--red-50); | |
| color: var(--red-600); | |
| border-color: var(--red-500); | |
| } | |
| /* Sidebar */ | |
| .sidebar-title { | |
| font-size: 1.125rem; | |
| font-weight: 600; | |
| color: var(--gray-900); | |
| margin-bottom: var(--space-4); | |
| } | |
| .tree-list { | |
| max-height: 70vh; | |
| overflow-y: auto; | |
| } | |
| .tree-item { | |
| padding: var(--space-4); | |
| border: 1px solid var(--gray-200); | |
| border-radius: var(--radius-md); | |
| margin-bottom: var(--space-3); | |
| background: white; | |
| transition: all 0.15s ease; | |
| } | |
| .tree-item:hover { | |
| border-color: var(--primary-500); | |
| box-shadow: var(--shadow-sm); | |
| transform: translateY(-1px); | |
| } | |
| .tree-header { | |
| display: flex; | |
| justify-content: space-between; | |
| align-items: center; | |
| margin-bottom: var(--space-2); | |
| } | |
| .tree-id { | |
| font-weight: 600; | |
| color: var(--primary-600); | |
| font-size: 0.875rem; | |
| } | |
| .tree-actions { | |
| display: flex; | |
| gap: var(--space-1); | |
| } | |
| .btn-icon { | |
| background: transparent; | |
| border: none; | |
| cursor: pointer; | |
| padding: var(--space-1); | |
| border-radius: var(--radius-sm); | |
| color: var(--gray-500); | |
| transition: all 0.15s ease; | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| } | |
| .btn-icon:hover { | |
| background: var(--gray-100); | |
| color: var(--gray-700); | |
| } | |
| .tree-info { | |
| color: var(--gray-600); | |
| font-size: 0.75rem; | |
| line-height: 1.4; | |
| } | |
| /* Loading States */ | |
| .loading { | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| padding: var(--space-8); | |
| color: var(--gray-500); | |
| font-size: 0.875rem; | |
| } | |
| .spinner { | |
| border: 2px solid var(--gray-200); | |
| border-top: 2px solid var(--primary-500); | |
| border-radius: 50%; | |
| width: 1rem; | |
| height: 1rem; | |
| animation: spin 1s linear infinite; | |
| margin-right: var(--space-2); | |
| } | |
| @keyframes spin { | |
| 0% { transform: rotate(0deg); } | |
| 100% { transform: rotate(360deg); } | |
| } | |
| /* Hidden */ | |
| .hidden { | |
| display: none; | |
| } | |
| /* Improved Mobile Responsiveness */ | |
| @media (max-width: 768px) { | |
| .header-content { | |
| padding: var(--space-3) var(--space-4); | |
| flex-direction: column; | |
| align-items: stretch; | |
| gap: var(--space-3); | |
| } | |
| .header h1 { | |
| font-size: 1.5rem; | |
| } | |
| .header-actions { | |
| flex-wrap: wrap; | |
| justify-content: space-between; | |
| width: 100%; | |
| } | |
| .main-container { | |
| padding: var(--space-4) var(--space-3); | |
| } | |
| .card-header, | |
| .card-content { | |
| padding: var(--space-4) var(--space-3); | |
| } | |
| .card-title { | |
| font-size: 1.125rem; | |
| } | |
| .section-title { | |
| font-size: 1rem; | |
| } | |
| .user-info { | |
| padding: var(--space-2) var(--space-3); | |
| flex-shrink: 0; | |
| } | |
| .user-details { | |
| display: none; | |
| } | |
| /* Form improvements for mobile */ | |
| .form-input, | |
| .form-textarea { | |
| font-size: 16px; /* Prevent zoom on iOS */ | |
| padding: var(--space-3) var(--space-3); | |
| } | |
| .form-label { | |
| font-size: 0.8125rem; | |
| margin-bottom: var(--space-1); | |
| } | |
| .multi-select { | |
| max-height: 150px; | |
| } | |
| .multi-select label { | |
| padding: var(--space-2) var(--space-3); | |
| margin: var(--space-1); | |
| font-size: 0.8125rem; | |
| } | |
| } | |
| @media (max-width: 480px) { | |
| .header h1 { | |
| font-size: 1.25rem; | |
| } | |
| .header-subtitle { | |
| font-size: 0.75rem; | |
| } | |
| .main-container { | |
| padding: var(--space-3) var(--space-2); | |
| } | |
| .card { | |
| border-radius: var(--radius-lg); | |
| } | |
| .card-header, | |
| .card-content { | |
| padding: var(--space-3) var(--space-2); | |
| } | |
| .btn { | |
| padding: var(--space-2) var(--space-3); | |
| font-size: 0.8125rem; | |
| } | |
| .btn-lg { | |
| padding: var(--space-3) var(--space-4); | |
| font-size: 0.875rem; | |
| } | |
| } | |
| /* Focus visible for accessibility */ | |
| .btn:focus-visible, | |
| .form-input:focus-visible, | |
| .btn-icon:focus-visible { | |
| outline: 2px solid var(--primary-500); | |
| outline-offset: 2px; | |
| } | |
| /* Autocomplete Dropdown Styles */ | |
| .autocomplete-container { | |
| position: relative; | |
| } | |
| .autocomplete-dropdown { | |
| position: absolute; | |
| top: 100%; | |
| left: 0; | |
| right: 0; | |
| z-index: 1000; | |
| background: white; | |
| border: 1px solid var(--gray-300); | |
| border-top: none; | |
| border-radius: 0 0 var(--radius-md) var(--radius-md); | |
| max-height: 200px; | |
| overflow-y: auto; | |
| box-shadow: var(--shadow-lg); | |
| display: none; | |
| } | |
| .autocomplete-item { | |
| padding: var(--space-3) var(--space-4); | |
| cursor: pointer; | |
| border-bottom: 1px solid var(--gray-100); | |
| transition: all 0.15s ease; | |
| } | |
| .autocomplete-item:hover, | |
| .autocomplete-item.highlighted { | |
| background: var(--primary-50); | |
| } | |
| .autocomplete-item:last-child { | |
| border-bottom: none; | |
| } | |
| .autocomplete-primary { | |
| font-size: 0.875rem; | |
| font-weight: 500; | |
| color: var(--gray-900); | |
| margin-bottom: var(--space-1); | |
| } | |
| .autocomplete-secondary { | |
| font-size: 0.75rem; | |
| color: var(--gray-600); | |
| margin-bottom: var(--space-2); | |
| } | |
| .autocomplete-badge { | |
| display: inline-block; | |
| background: var(--primary-100); | |
| color: var(--primary-700); | |
| padding: var(--space-1) var(--space-2); | |
| border-radius: var(--radius-sm); | |
| font-size: 0.75rem; | |
| font-weight: 500; | |
| margin-right: var(--space-1); | |
| margin-bottom: var(--space-1); | |
| } | |
| .autocomplete-loading, | |
| .autocomplete-no-results { | |
| padding: var(--space-3) var(--space-4); | |
| text-align: center; | |
| color: var(--gray-500); | |
| font-size: 0.875rem; | |
| } | |
| /* Improved Tree Item Actions */ | |
| .tree-actions { | |
| display: flex; | |
| gap: var(--space-1); | |
| flex-shrink: 0; | |
| } | |
| .btn-icon { | |
| padding: var(--space-1) var(--space-2); | |
| font-size: 0.75rem; | |
| font-weight: 500; | |
| min-width: 44px; /* Ensure touch-friendly minimum */ | |
| height: 32px; | |
| border: 1px solid var(--gray-300); | |
| background: white; | |
| border-radius: var(--radius-sm); | |
| text-align: center; | |
| transition: all 0.15s ease; | |
| } | |
| .btn-icon.edit-tree { | |
| color: var(--primary-600); | |
| border-color: var(--primary-200); | |
| } | |
| .btn-icon.edit-tree:hover { | |
| background: var(--primary-50); | |
| border-color: var(--primary-300); | |
| color: var(--primary-700); | |
| } | |
| .btn-icon.delete-tree { | |
| color: var(--red-600); | |
| border-color: var(--red-200); | |
| } | |
| .btn-icon.delete-tree:hover { | |
| background: var(--red-50); | |
| border-color: var(--red-300); | |
| color: var(--red-700); | |
| } | |
| /* Mobile Improvements for Tree Items */ | |
| @media (max-width: 768px) { | |
| .tree-header { | |
| flex-direction: column; | |
| align-items: flex-start; | |
| gap: var(--space-2); | |
| } | |
| .tree-actions { | |
| align-self: flex-end; | |
| gap: var(--space-2); | |
| } | |
| .btn-icon { | |
| min-width: 48px; | |
| height: 36px; | |
| font-size: 0.8125rem; | |
| } | |
| } | |
| /* Enhanced Photo Category Styling */ | |
| .photo-category { | |
| margin-bottom: var(--space-6); | |
| padding: var(--space-5); | |
| border: 2px solid var(--gray-200); | |
| border-radius: var(--radius-xl); | |
| background: white; | |
| box-shadow: var(--shadow-sm); | |
| transition: all 0.2s ease; | |
| } | |
| .photo-category:hover { | |
| border-color: var(--primary-200); | |
| box-shadow: var(--shadow-md); | |
| } | |
| .photo-category-header { | |
| display: flex; | |
| justify-content: space-between; | |
| align-items: center; | |
| margin-bottom: var(--space-4); | |
| padding-bottom: var(--space-3); | |
| border-bottom: 1px solid var(--gray-100); | |
| } | |
| .photo-category-title { | |
| font-size: 1rem; | |
| font-weight: 600; | |
| color: var(--gray-800); | |
| display: flex; | |
| align-items: center; | |
| gap: var(--space-2); | |
| } | |
| .photo-category-icon { | |
| width: 20px; | |
| height: 20px; | |
| background: var(--primary-100); | |
| border-radius: var(--radius-sm); | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| font-size: 0.75rem; | |
| } | |
| .photo-upload-area { | |
| display: flex; | |
| gap: var(--space-3); | |
| } | |
| .photo-upload { | |
| flex: 1; | |
| background: var(--gray-50); | |
| border: 2px dashed var(--gray-300); | |
| border-radius: var(--radius-lg); | |
| padding: var(--space-4); | |
| text-align: center; | |
| cursor: pointer; | |
| color: var(--gray-600); | |
| font-size: 0.875rem; | |
| transition: all 0.2s ease; | |
| min-height: 80px; | |
| display: flex; | |
| flex-direction: column; | |
| justify-content: center; | |
| align-items: center; | |
| gap: var(--space-2); | |
| } | |
| .photo-upload:hover { | |
| border-color: var(--primary-400); | |
| background: var(--primary-50); | |
| color: var(--primary-600); | |
| } | |
| .photo-upload-icon { | |
| font-size: 1.5rem; | |
| opacity: 0.6; | |
| } | |
| .camera-btn { | |
| padding: var(--space-2) var(--space-4); | |
| background: var(--primary-500); | |
| color: white; | |
| border: none; | |
| border-radius: var(--radius-md); | |
| font-size: 0.8125rem; | |
| font-weight: 500; | |
| cursor: pointer; | |
| transition: all 0.2s ease; | |
| min-width: 80px; | |
| } | |
| .camera-btn:hover { | |
| background: var(--primary-600); | |
| transform: translateY(-1px); | |
| } | |
| .uploaded-file { | |
| background: var(--green-50); | |
| color: var(--green-700); | |
| padding: var(--space-2) var(--space-3); | |
| border-radius: var(--radius-md); | |
| font-size: 0.8125rem; | |
| margin-top: var(--space-3); | |
| border: 1px solid var(--green-200); | |
| display: flex; | |
| align-items: center; | |
| gap: var(--space-2); | |
| } | |
| .uploaded-file::before { | |
| content: '✓'; | |
| font-weight: bold; | |
| } | |
| /* Demo user disabled button styles */ | |
| .tt-btn-demo-disabled { | |
| background: var(--gray-200) ; | |
| color: var(--gray-500) ; | |
| cursor: not-allowed ; | |
| border: 1px solid var(--gray-300) ; | |
| position: relative; | |
| overflow: hidden; | |
| } | |
| .tt-btn-demo-disabled:hover { | |
| background: var(--gray-200) ; | |
| color: var(--gray-500) ; | |
| transform: none ; | |
| box-shadow: none ; | |
| } | |
| .demo-notice { | |
| background: var(--primary-50); | |
| border: 1px solid var(--primary-200); | |
| color: var(--primary-800); | |
| padding: var(--space-4) var(--space-5); | |
| border-radius: var(--radius-lg); | |
| font-size: 0.9rem; | |
| margin-bottom: var(--space-5); | |
| display: flex; | |
| align-items: flex-start; | |
| gap: var(--space-3); | |
| line-height: 1.5; | |
| } | |
| .demo-notice-icon { | |
| width: 24px; | |
| height: 24px; | |
| background: var(--primary-500); | |
| color: white; | |
| border-radius: 50%; | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| font-size: 0.875rem; | |
| font-weight: 600; | |
| flex-shrink: 0; | |
| margin-top: 1px; | |
| } | |
| </style> | |
| <script> | |
| // Force refresh if we detect cached version | |
| (function() { | |
| const currentVersion = '5.1.1'; | |
| const timestamp = '1762284025'; // Cache-busting bump | |
| const lastVersion = sessionStorage.getItem('treetrack_version'); | |
| const lastTimestamp = sessionStorage.getItem('treetrack_timestamp'); | |
| if (!lastVersion || lastVersion !== currentVersion || !lastTimestamp || lastTimestamp !== timestamp) { | |
| // Clear all storage to force fresh start | |
| sessionStorage.clear(); | |
| localStorage.removeItem('treetrack_cache'); | |
| sessionStorage.setItem('treetrack_version', currentVersion); | |
| sessionStorage.setItem('treetrack_timestamp', timestamp); | |
| if (lastVersion && lastVersion !== currentVersion) { | |
| // Force hard refresh | |
| location.reload(true); | |
| return; | |
| } | |
| } | |
| })(); | |
| </script> | |
| </head> | |
| <body> | |
| <div class="tt-header" style="background-image: url('https://images.unsplash.com/photo-1441974231531-c6227db76b6e?q=80&w=1920&auto=format&fit=crop&ixlib=rb-4.0.3'); background-size: cover; background-position: center;"> | |
| <div class="tt-header-content"> | |
| <div class="tt-header-brand"> | |
| <div class="tt-header-logo">TreeTrack</div> | |
| <div class="tt-header-subtitle">Professional Field Research Platform</div> | |
| </div> | |
| <div class="tt-header-actions"> | |
| <div class="tt-user-info" id="userInfo"> | |
| <div class="tt-user-avatar" id="userAvatar">U</div> | |
| <div class="tt-user-details"> | |
| <div class="tt-user-name" id="userName">Loading...</div> | |
| <div class="tt-user-role" id="userRole">User</div> | |
| </div> | |
| </div> | |
| <a href="/welcome" class="tt-btn tt-btn-secondary" id="welcomeBtn" style="display: none;">Back to Homepage</a> | |
| <a href="/static/map.html" class="tt-btn tt-btn-secondary" id="viewMapBtn">View on Map</a> | |
| <button id="logoutBtn" class="tt-btn tt-btn-secondary">Logout</button> | |
| </div> | |
| </div> | |
| </div> | |
| <div class="main-container"> | |
| <div class="content-grid"> | |
| <div class="tt-card"> | |
| <div class="tt-card-header"> | |
| <h2 class="tt-card-title">Tree Documentation Form</h2> | |
| <p class="tt-card-subtitle">Complete the form below to document tree specimens in the field. All required fields are marked with an asterisk.</p> | |
| </div> | |
| <div class="tt-card-content"> | |
| <form id="treeForm"> | |
| <!-- Location Section --> | |
| <div class="form-section"> | |
| <div class="section-header"> | |
| <h3 class="section-title">Geographic Location</h3> | |
| <p class="section-description">Precise coordinates are essential for mapping and field research</p> | |
| </div> | |
| <div class="form-row"> | |
| <div class="form-group"> | |
| <label class="form-label required" for="latitude">Latitude</label> | |
| <input type="number" id="latitude" class="form-input" step="0.0000001" min="-90" max="90" required placeholder="e.g. 26.1445"> | |
| </div> | |
| <div class="form-group"> | |
| <label class="form-label required" for="longitude">Longitude</label> | |
| <input type="number" id="longitude" class="form-input" step="0.0000001" min="-180" max="180" required placeholder="e.g. 91.7362"> | |
| </div> | |
| </div> | |
| <div class="form-group"> | |
| <label class="form-label" for="locationName">Location Name (Optional)</label> | |
| <input type="text" id="locationName" class="form-input" placeholder="e.g., Near Old Banyan, Village Center"> | |
| </div> | |
| <div class="form-group"> | |
| <div class="location-buttons"> | |
| <button type="button" id="getLocation" class="tt-btn tt-btn-outline location-btn-gps"> | |
| <span class="btn-text">Get GPS Location</span> | |
| </button> | |
| <a href="/static/map.html" class="tt-btn tt-btn-primary location-btn-map"> | |
| <span class="btn-text">Select from Map</span> | |
| </a> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Identification Section --> | |
| <div class="form-section"> | |
| <div class="section-header"> | |
| <h3 class="section-title">Tree Identification</h3> | |
| <p class="section-description">Botanical and local identification details</p> | |
| </div> | |
| <div class="form-group"> | |
| <label class="form-label" for="localName">Local Name (Assamese)</label> | |
| <input type="text" id="localName" class="form-input" placeholder="Enter local Assamese name"> | |
| </div> | |
| <div class="form-group"> | |
| <label class="form-label" for="scientificName">Scientific Name</label> | |
| <input type="text" id="scientificName" class="form-input" placeholder="e.g., Ficus benghalensis"> | |
| </div> | |
| <div class="form-group"> | |
| <label class="form-label" for="commonName">Common Name</label> | |
| <input type="text" id="commonName" class="form-input" placeholder="e.g., Banyan Tree"> | |
| </div> | |
| <div class="form-group"> | |
| <label class="form-label" for="treeCode">Tree Reference Code</label> | |
| <input type="text" id="treeCode" class="form-input" placeholder="e.g., C.A, A-G1" maxlength="20"> | |
| </div> | |
| </div> | |
| <!-- Measurements Section --> | |
| <div class="form-section"> | |
| <div class="section-header"> | |
| <h3 class="section-title">Physical Measurements</h3> | |
| <p class="section-description">Quantitative assessment of tree dimensions</p> | |
| </div> | |
| <div class="form-row"> | |
| <div class="form-group"> | |
| <label class="form-label" for="height">Height (ft)</label> | |
| <input type="number" id="height" class="form-input" step="0.1" min="0" placeholder="e.g., 50.0"> | |
| </div> | |
| <div class="form-group"> | |
| <label class="form-label" for="width">Girth/DBH (ft)</label> | |
| <input type="number" id="width" class="form-input" step="0.1" min="0" placeholder="e.g., 3.5"> | |
| </div> | |
| </div> | |
| <p class="section-description">Note: Enter measurements in feet. If you have metric values, you can convert: 1 meter ≈ 3.28084 ft; 1 cm ≈ 0.0328084 ft.</p> | |
| </div> | |
| <!-- Utility Section --> | |
| <div class="form-section"> | |
| <div class="section-header"> | |
| <h3 class="section-title">Ecological & Cultural Utility</h3> | |
| <p class="section-description">Select all applicable ecological and cultural uses</p> | |
| </div> | |
| <div class="form-group"> | |
| <div id="utilityOptions" class="multi-select"> | |
| <!-- Options loaded dynamically --> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Phenology Section --> | |
| <div class="form-section"> | |
| <div class="section-header"> | |
| <h3 class="section-title">Phenology Assessment</h3> | |
| <p class="section-description">Current developmental stages observed</p> | |
| </div> | |
| <div class="form-group"> | |
| <div id="phenologyOptions" class="multi-select"> | |
| <!-- Options loaded dynamically --> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Photography Section --> | |
| <div class="form-section"> | |
| <div class="section-header"> | |
| <h3 class="section-title">Photographic Documentation</h3> | |
| <p class="section-description">Upload photographs for different tree components</p> | |
| </div> | |
| <div id="photoCategories"> | |
| <!-- Photo categories loaded dynamically --> | |
| </div> | |
| </div> | |
| <!-- Cultural Documentation Section --> | |
| <div class="form-section"> | |
| <div class="section-header"> | |
| <h3 class="section-title">Cultural Documentation</h3> | |
| <p class="section-description">Stories, histories, and cultural significance</p> | |
| </div> | |
| <div class="form-group"> | |
| <label class="form-label" for="storytellingText">Stories, Histories & Narratives</label> | |
| <textarea id="storytellingText" class="form-input form-textarea" placeholder="Share any stories, historical context, or cultural significance..." maxlength="5000"></textarea> | |
| </div> | |
| <div class="form-group"> | |
| <label class="form-label">Audio Recording</label> | |
| <div class="file-upload-area" id="audioUpload"> | |
| <div class="file-upload-icon">MIC</div> | |
| <div class="file-upload-text">Click to upload audio file</div> | |
| <div class="file-upload-hint">Or drag and drop (MP3, WAV, M4A)</div> | |
| </div> | |
| <div style="display:flex; align-items:center; gap: 0.75rem; margin-top: 0.75rem;"> | |
| <button type="button" id="recordBtn" class="tt-btn tt-btn-secondary">Record</button> | |
| <span id="recordingStatus" style="font-size:0.9rem; color: var(--gray-600);">Click to start recording</span> | |
| </div> | |
| <audio id="audioPlayback" controls class="hidden" style="margin-top: 0.75rem; width: 100%;"></audio> | |
| <div id="audioUploadResult"></div> | |
| </div> | |
| </div> | |
| <!-- Field Notes Section --> | |
| <div class="form-section"> | |
| <div class="section-header"> | |
| <h3 class="section-title">Field Notes</h3> | |
| <p class="section-description">Additional observations and remarks</p> | |
| </div> | |
| <div class="form-group"> | |
| <label class="form-label" for="notes">Additional Observations</label> | |
| <textarea id="notes" class="form-input form-textarea" placeholder="Any additional observations, notes, or remarks..." maxlength="2000"></textarea> | |
| </div> | |
| </div> | |
| <div class="form-actions"> | |
| <button type="button" id="resetForm" class="tt-btn tt-btn-outline">Reset Form</button> | |
| <button type="submit" class="tt-btn tt-btn-primary tt-btn-lg">Save Tree Record</button> | |
| </div> | |
| </form> | |
| <div id="message"></div> | |
| </div> | |
| </div> | |
| <div class="tt-card"> | |
| <div class="tt-card-header"> | |
| <h3 class="tt-card-title">Recent Trees</h3> | |
| <p class="tt-card-subtitle">Recently documented specimens</p> | |
| </div> | |
| <div class="tt-card-content"> | |
| <div id="treeList" class="tree-list"> | |
| <div class="tt-loading"> | |
| <div class="tt-spinner"></div> | |
| Loading trees... | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <script type="module" src="/static/js/tree-track-app.js?v=5.1.1&t=1762284025"></script> | |
| <script> | |
| // Idle-time prefetch of map assets to speed up first navigation | |
| (function prewarm() { | |
| const rIC = window.requestIdleCallback || function(cb){return setTimeout(cb, 500)}; | |
| rIC(() => { | |
| const assets = [ | |
| '/static/map.html', | |
| 'https://unpkg.com/leaflet@1.9.4/dist/leaflet.css', | |
| 'https://unpkg.com/leaflet@1.9.4/dist/leaflet.js', | |
| '/static/map.js?v=5.1.1&t=1755114163' | |
| ]; | |
| assets.forEach(url => { try { fetch(url, {cache: 'force-cache'}); } catch(_) {} }); | |
| }); | |
| })(); | |
| // Prefetch on intent (hover/touch) on View Map button | |
| (function prefetchOnIntent(){ | |
| const link = document.getElementById('viewMapBtn'); | |
| if (!link) return; | |
| const prefetch = () => { try { fetch('/static/map.html', {cache: 'force-cache'}); } catch(_) {} }; | |
| link.addEventListener('mouseenter', prefetch, {once: true}); | |
| link.addEventListener('touchstart', prefetch, {once: true}); | |
| })(); | |
| </script> | |
| </body> | |
| </html> | |