Spaces:
Runtime error
Runtime error
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>TreeTrack - Forest Research Platform</title> | |
| <link rel="stylesheet" href="/static/css/design-system.css"> | |
| <link rel="preconnect" href="https://fonts.googleapis.com"> | |
| <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin> | |
| <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet"> | |
| <!-- Leaflet CSS for interactive map --> | |
| <link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css" crossorigin=""> | |
| <style> | |
| /* Conference Welcome Screen Styles */ | |
| :root { | |
| --forest-primary: #1a2e1a; | |
| --forest-secondary: #2d4a2d; | |
| --earth-accent: #4a5d4a; | |
| --sky-light: #f0f6f0; | |
| --soft-red: #dc3545; | |
| --cream-white: #fefefe; | |
| } | |
| body { | |
| margin: 0; | |
| font-family: 'Inter', sans-serif; | |
| min-height: 100vh; | |
| overflow-x: hidden; | |
| position: relative; | |
| } | |
| /* Background Map Display */ | |
| #background-map { | |
| position: fixed; | |
| top: 0; | |
| left: 0; | |
| width: 100%; | |
| height: 100%; | |
| z-index: 0; | |
| opacity: 0; | |
| transition: opacity 1s ease-in-out; | |
| } | |
| #background-map.loaded { | |
| opacity: 1; | |
| } | |
| /* Overlay for better text contrast */ | |
| .map-overlay { | |
| position: fixed; | |
| top: 0; | |
| left: 0; | |
| width: 100%; | |
| height: 100%; | |
| background: linear-gradient(135deg, | |
| rgba(26, 46, 26, 0.75) 0%, | |
| rgba(45, 74, 45, 0.65) 60%, | |
| rgba(74, 93, 74, 0.7) 100%); | |
| z-index: 1; | |
| pointer-events: none; | |
| } | |
| /* Content overlay with enhanced glassmorphism */ | |
| .content-overlay { | |
| position: relative; | |
| z-index: 2; | |
| min-height: 100vh; | |
| backdrop-filter: blur(1px); | |
| } | |
| /* Hero Section */ | |
| .hero-section { | |
| min-height: 100vh; | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| position: relative; | |
| background: transparent; | |
| } | |
| .welcome-container { | |
| max-width: 1200px; | |
| width: 90%; | |
| text-align: center; | |
| color: white; | |
| z-index: 2; | |
| } | |
| .hero-badge { | |
| display: inline-block; | |
| background: rgba(255, 255, 255, 0.1); | |
| backdrop-filter: blur(10px); | |
| padding: 8px 20px; | |
| border-radius: 50px; | |
| font-size: 0.875rem; | |
| font-weight: 500; | |
| margin-bottom: 2rem; | |
| border: 1px solid rgba(255, 255, 255, 0.2); | |
| } | |
| .hero-title { | |
| font-size: clamp(3rem, 8vw, 5rem); | |
| font-weight: 700; | |
| margin-bottom: 1rem; | |
| line-height: 1.1; | |
| background: linear-gradient(135deg, white, rgba(255, 255, 255, 0.8)); | |
| -webkit-background-clip: text; | |
| -webkit-text-fill-color: transparent; | |
| background-clip: text; | |
| } | |
| .hero-subtitle { | |
| font-size: clamp(1.125rem, 3vw, 1.5rem); | |
| margin-bottom: 1rem; | |
| opacity: 0.9; | |
| font-weight: 400; | |
| } | |
| .hero-description { | |
| font-size: 1.125rem; | |
| margin-bottom: 3rem; | |
| opacity: 0.8; | |
| max-width: 600px; | |
| margin-left: auto; | |
| margin-right: auto; | |
| line-height: 1.6; | |
| } | |
| /* Exploration Grid */ | |
| .exploration-grid { | |
| display: grid; | |
| grid-template-columns: repeat(3, 1fr); | |
| gap: 1.5rem; | |
| margin-top: 3rem; | |
| max-width: 1000px; | |
| margin-left: auto; | |
| margin-right: auto; | |
| } | |
| .exploration-card { | |
| background: rgba(255, 255, 255, 0.12); | |
| backdrop-filter: blur(20px); | |
| -webkit-backdrop-filter: blur(20px); | |
| border: 1px solid rgba(255, 255, 255, 0.25); | |
| border-radius: 1rem; | |
| padding: 2rem; | |
| cursor: pointer; | |
| transition: all 0.3s ease; | |
| text-align: center; | |
| position: relative; | |
| overflow: hidden; | |
| min-height: 180px; | |
| display: flex; | |
| flex-direction: column; | |
| justify-content: center; | |
| align-items: center; | |
| box-shadow: 0 8px 32px rgba(0, 0, 0, 0.15); | |
| } | |
| .exploration-card::before { | |
| content: ''; | |
| position: absolute; | |
| top: 0; | |
| left: 0; | |
| right: 0; | |
| height: 4px; | |
| background: linear-gradient(90deg, var(--energy-orange), var(--earth-accent)); | |
| transform: scaleX(0); | |
| transition: transform 0.3s ease; | |
| } | |
| .exploration-card:hover::before { | |
| transform: scaleX(1); | |
| } | |
| .exploration-card:hover { | |
| transform: translateY(-3px); | |
| background: rgba(255, 255, 255, 0.18); | |
| border-color: rgba(255, 255, 255, 0.35); | |
| box-shadow: 0 15px 40px rgba(0, 0, 0, 0.25); | |
| } | |
| .exploration-card:hover .card-icon { | |
| background: rgba(255, 255, 255, 0.25); | |
| border-color: rgba(255, 255, 255, 0.5); | |
| transform: translateY(-2px); | |
| } | |
| .card-icon { | |
| width: 60px; | |
| height: 60px; | |
| background: rgba(255, 255, 255, 0.15); | |
| border: 1px solid rgba(255, 255, 255, 0.3); | |
| border-radius: 12px; | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| margin: 0 auto 1.5rem auto; | |
| font-size: 0.75rem; | |
| font-weight: 500; | |
| color: rgba(255, 255, 255, 0.9); | |
| letter-spacing: 0.5px; | |
| transition: all 0.3s ease; | |
| } | |
| .card-title { | |
| font-size: 1.25rem; | |
| font-weight: 600; | |
| margin-bottom: 0.75rem; | |
| color: white; | |
| } | |
| .card-description { | |
| font-size: 1rem; | |
| opacity: 0.9; | |
| line-height: 1.6; | |
| text-align: center; | |
| margin-bottom: 0; | |
| } | |
| /* Research Context */ | |
| .research-context { | |
| background: rgba(255, 255, 255, 0.08); | |
| backdrop-filter: blur(15px); | |
| -webkit-backdrop-filter: blur(15px); | |
| border: 1px solid rgba(255, 255, 255, 0.2); | |
| border-radius: 1rem; | |
| padding: 1.5rem; | |
| margin-top: 3rem; | |
| text-align: center; | |
| box-shadow: 0 8px 32px rgba(0, 0, 0, 0.1); | |
| } | |
| .context-badge { | |
| background: var(--energy-orange); | |
| color: white; | |
| padding: 4px 12px; | |
| border-radius: 20px; | |
| font-size: 0.75rem; | |
| font-weight: 600; | |
| text-transform: uppercase; | |
| letter-spacing: 0.5px; | |
| display: inline-block; | |
| margin-bottom: 1rem; | |
| } | |
| .context-text { | |
| font-size: 1rem; | |
| opacity: 0.9; | |
| line-height: 1.6; | |
| } | |
| /* Responsive Design */ | |
| @media (max-width: 1024px) and (min-width: 769px) { | |
| .exploration-grid { | |
| gap: 1rem; | |
| } | |
| .exploration-card { | |
| padding: 1.5rem; | |
| min-height: 160px; | |
| } | |
| } | |
| @media (max-width: 768px) { | |
| .welcome-container { | |
| width: 95%; | |
| } | |
| .exploration-grid { | |
| grid-template-columns: 1fr; | |
| gap: 1.5rem; | |
| } | |
| .exploration-card { | |
| padding: 1.5rem; | |
| } | |
| } | |
| /* Loading Animation */ | |
| .card-loading { | |
| opacity: 0.7; | |
| pointer-events: none; | |
| } | |
| .card-loading::after { | |
| content: ''; | |
| position: absolute; | |
| top: 50%; | |
| left: 50%; | |
| width: 20px; | |
| height: 20px; | |
| margin: -10px 0 0 -10px; | |
| border: 2px solid transparent; | |
| border-top: 2px solid white; | |
| border-radius: 50%; | |
| animation: spin 1s linear infinite; | |
| } | |
| @keyframes spin { | |
| 0% { transform: rotate(0deg); } | |
| 100% { transform: rotate(360deg); } | |
| } | |
| /* Logout Button */ | |
| .logout-btn { | |
| position: fixed; | |
| top: 2rem; | |
| right: 2rem; | |
| z-index: 1000; | |
| background: rgba(220, 53, 69, 0.9); | |
| color: white; | |
| border: 1px solid rgba(255, 255, 255, 0.3); | |
| padding: 0.75rem 1.5rem; | |
| border-radius: 0.5rem; | |
| cursor: pointer; | |
| font-weight: 600; | |
| font-size: 0.875rem; | |
| backdrop-filter: blur(10px); | |
| transition: all 0.2s ease; | |
| } | |
| .logout-btn:hover { | |
| background: rgba(185, 28, 28, 0.95); | |
| transform: translateY(-1px); | |
| box-shadow: 0 4px 15px rgba(220, 53, 69, 0.3); | |
| } | |
| @media (max-width: 768px) { | |
| .logout-btn { | |
| top: 1rem; | |
| right: 1rem; | |
| padding: 0.5rem 1rem; | |
| font-size: 0.8rem; | |
| } | |
| } | |
| </style> | |
| </head> | |
| <body> | |
| <!-- Background Map --> | |
| <div id="background-map"></div> | |
| <!-- Overlay for Better Text Contrast --> | |
| <div class="map-overlay"></div> | |
| <!-- Content Overlay --> | |
| <div class="content-overlay"> | |
| <!-- Logout Button --> | |
| <button class="logout-btn" id="logoutBtn">Logout</button> | |
| <div class="hero-section"> | |
| <div class="welcome-container"> | |
| <div class="hero-badge">Phase 1 Research • Tezpur, Assam</div> | |
| <h1 class="hero-title">TreeTrack</h1> | |
| <div class="hero-subtitle">Welcome to TreeTrack!</div> | |
| <p class="hero-description"> | |
| This app is designed for data collection and visualisation of various tree species and their distribution.<br> | |
| Here, you can explore Phase 1 of tree mapping efforts from various locations within Tezpur, Assam. | |
| </p> | |
| <div class="exploration-grid"> | |
| <div class="exploration-card" id="exploreForm" onclick="exploreDataCollection()"> | |
| <div class="card-icon">FORM</div> | |
| <div class="card-title">Data Collection Form</div> | |
| <div class="card-description"> | |
| View the data collection form used in the project | |
| </div> | |
| </div> | |
| <div class="exploration-card" id="exploreMap" onclick="exploreTreeMap()"> | |
| <div class="card-icon">MAP</div> | |
| <div class="card-title">Interactive Tree Map</div> | |
| <div class="card-description"> | |
| View an interactive map showing geotagged trees from Tezpur, Assam | |
| </div> | |
| </div> | |
| <div class="exploration-card" id="exploreContributors" onclick="exploreContributors()"> | |
| <div class="card-icon">TEAM</div> | |
| <div class="card-title">Contributors</div> | |
| <div class="card-description"> | |
| Meet the research team and project contributors | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Get in Touch Section --> | |
| <div class="research-context"> | |
| <div class="context-badge">Get in Touch</div> | |
| <div class="context-text"> | |
| <p style="margin-bottom: 1rem;">Thank you!</p> | |
| <p style="margin-bottom: 1rem;">Would you like to map trees in your own area?</p> | |
| <p style="margin-bottom: 1rem;">Do you think this app could be useful to you?</p> | |
| <p style="margin-bottom: 0;">Share your thoughts or suggestions at <a href="mailto:youthforforest2@gmail.com" style="color: rgba(255, 255, 255, 0.9); text-decoration: underline;">youthforforest2@gmail.com</a></p> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Leaflet JavaScript --> | |
| <script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js" crossorigin=""></script> | |
| <script> | |
| // Initialize Background Map Display | |
| function initializeMap() { | |
| // Tezpur, Assam coordinates | |
| const tezpurCoords = [26.6340, 92.7840]; | |
| // Create map instance for background display | |
| const map = L.map('background-map', { | |
| center: tezpurCoords, | |
| zoom: 12, | |
| zoomControl: false, | |
| scrollWheelZoom: false, | |
| doubleClickZoom: false, | |
| boxZoom: false, | |
| keyboard: false, | |
| dragging: false, | |
| touchZoom: false, | |
| attributionControl: false | |
| }); | |
| // Disable all interactions for decorative display only | |
| map.getContainer().style.cursor = 'default'; | |
| // Add satellite/terrain tile layer for background | |
| const satelliteLayer = L.tileLayer('https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}', { | |
| maxZoom: 19, | |
| attribution: false | |
| }); | |
| const terrainLayer = L.tileLayer('https://{s}.tile.opentopomap.org/{z}/{x}/{y}.png', { | |
| maxZoom: 17, | |
| attribution: false | |
| }); | |
| // Use satellite as primary, terrain as fallback | |
| satelliteLayer.on('tileerror', function() { | |
| console.log('Satellite layer failed, switching to terrain...'); | |
| map.removeLayer(satelliteLayer); | |
| terrainLayer.addTo(map); | |
| }); | |
| satelliteLayer.addTo(map); | |
| // Add decorative markers for Tezpur area | |
| const areaPoints = [ | |
| {coords: [26.6340, 92.7840], name: "Tezpur Center"}, | |
| {coords: [26.6500, 92.7900], name: "Research Area North"}, | |
| {coords: [26.6200, 92.7700], name: "Research Area South"}, | |
| {coords: [26.6400, 92.8000], name: "Research Area East"} | |
| ]; | |
| areaPoints.forEach(point => { | |
| L.circleMarker(point.coords, { | |
| radius: 3, | |
| fillColor: '#8ab070', | |
| color: '#739b5a', | |
| weight: 1, | |
| opacity: 0.6, | |
| fillOpacity: 0.4 | |
| }).addTo(map); | |
| }); | |
| // Fade in map when tiles are loaded | |
| satelliteLayer.on('load', function() { | |
| setTimeout(() => { | |
| document.getElementById('background-map').classList.add('loaded'); | |
| }, 500); | |
| }); | |
| // Fallback fade-in after 3 seconds | |
| setTimeout(() => { | |
| document.getElementById('background-map').classList.add('loaded'); | |
| }, 3000); | |
| } | |
| // Navigation functions | |
| function exploreDataCollection() { | |
| console.log('Exploring data collection form...'); | |
| const card = document.getElementById('exploreForm'); | |
| card.classList.add('card-loading'); | |
| // Try /form first, fallback to / if needed | |
| window.location.href = '/form'; | |
| } | |
| function exploreTreeMap() { | |
| console.log('Exploring tree map...'); | |
| const card = document.getElementById('exploreMap'); | |
| card.classList.add('card-loading'); | |
| window.location.href = '/map'; | |
| } | |
| function exploreContributors() { | |
| console.log('Exploring contributors page...'); | |
| const card = document.getElementById('exploreContributors'); | |
| card.classList.add('card-loading'); | |
| window.location.href = '/contributors'; | |
| } | |
| // Logout function | |
| async function logout() { | |
| try { | |
| const token = localStorage.getItem('auth_token'); | |
| if (token) { | |
| await fetch('/api/auth/logout', { | |
| method: 'POST', | |
| headers: { | |
| 'Authorization': `Bearer ${token}` | |
| } | |
| }); | |
| } | |
| } catch (error) { | |
| console.error('Logout error:', error); | |
| } finally { | |
| localStorage.removeItem('auth_token'); | |
| localStorage.removeItem('user_info'); | |
| window.location.href = '/login'; | |
| } | |
| } | |
| // Enhanced mobile interactions | |
| if ('ontouchstart' in window) { | |
| document.querySelectorAll('.exploration-card').forEach(card => { | |
| card.addEventListener('touchstart', function() { | |
| this.style.transform = 'translateY(-2px)'; | |
| }); | |
| card.addEventListener('touchend', function() { | |
| setTimeout(() => { | |
| this.style.transform = ''; | |
| }, 150); | |
| }); | |
| }); | |
| } | |
| // Keyboard navigation | |
| document.addEventListener('keydown', function(e) { | |
| if (e.key === '1') exploreDataCollection(); | |
| if (e.key === '2') exploreTreeMap(); | |
| if (e.key === '3') exploreContributors(); | |
| if (e.key === 'Escape') window.location.reload(); | |
| }); | |
| // Demo session management | |
| function setupDemoSession() { | |
| // Set session marker | |
| sessionStorage.setItem('demo_session', 'active'); | |
| sessionStorage.setItem('session_start', Date.now()); | |
| // Auto-refresh every 8 hours to prevent stale sessions | |
| setTimeout(() => { | |
| window.location.reload(); | |
| }, 8 * 60 * 60 * 1000); | |
| } | |
| // Initialize demo mode | |
| setupDemoSession(); | |
| // Initialize map when page loads | |
| document.addEventListener('DOMContentLoaded', function() { | |
| // Small delay to ensure DOM is fully ready | |
| setTimeout(initializeMap, 200); | |
| // Setup logout button | |
| const logoutBtn = document.getElementById('logoutBtn'); | |
| if (logoutBtn) { | |
| logoutBtn.addEventListener('click', logout); | |
| } | |
| }); | |
| // Fallback initialization | |
| window.addEventListener('load', function() { | |
| if (!document.getElementById('background-map').hasChildNodes()) { | |
| initializeMap(); | |
| } | |
| }); | |
| </script> | |
| </body> | |
| </html> |