/** * UI Manager Module * Handles user interface updates, messages, and tree list display */ export class UIManager { constructor(authManager) { this.authManager = authManager; } initialize() { this.displayUserInfo(); this.loadSelectedLocation(); this.setupDemoUserUI(); } displayUserInfo() { if (!this.authManager.currentUser) return; const userNameEl = document.getElementById('userName'); const userRoleEl = document.getElementById('userRole'); const userAvatarEl = document.getElementById('userAvatar'); if (userNameEl) { userNameEl.textContent = this.authManager.currentUser.full_name; } if (userRoleEl) { userRoleEl.textContent = this.authManager.currentUser.role; } if (userAvatarEl) { userAvatarEl.textContent = this.authManager.currentUser.full_name.charAt(0).toUpperCase(); } } loadSelectedLocation() { const selectedLocation = localStorage.getItem('selectedLocation'); if (selectedLocation) { try { const location = JSON.parse(selectedLocation); const latElement = document.getElementById('latitude'); const lngElement = document.getElementById('longitude'); if (latElement && lngElement) { latElement.value = location.lat.toFixed(6); lngElement.value = location.lng.toFixed(6); // Clear the stored location localStorage.removeItem('selectedLocation'); this.showMessage('Location loaded from map!', 'success'); } } catch (error) { console.error('Error loading selected location:', error); } } } showMessage(message, type) { const messageDiv = document.getElementById('message'); if (!messageDiv) return; messageDiv.className = `message ${type === 'error' ? 'error' : 'success'}`; messageDiv.textContent = message; // Auto-hide after 5 seconds setTimeout(() => { messageDiv.textContent = ''; messageDiv.className = ''; }, 5000); } renderTreeList(trees) { const treeList = document.getElementById('treeList'); if (!treeList) return; if (trees.length === 0) { treeList.innerHTML = '
No trees recorded yet
'; return; } // Compute display numbers for Ishita's trees created today const now = new Date(); const y = now.getFullYear(), m = now.getMonth(), d = now.getDate(); const isToday = (ts) => { try { const t=new Date(ts); return t.getFullYear()===y && t.getMonth()===m && t.getDate()===d; } catch(_) { return false; } }; const ishitaToday = trees.filter(t => (t.created_by||'').toLowerCase()==='ishita' && isToday(t.created_at)); // Sort by created_at ascending to assign small to early ones ishitaToday.sort((a,b) => new Date(a.created_at) - new Date(b.created_at)); const ishitaIndex = new Map(); ishitaToday.forEach((t, idx) => ishitaIndex.set(t.id, idx+1)); treeList.innerHTML = trees.map(tree => { const canEdit = this.authManager.canEditTree(tree.created_by); const canDelete = this.authManager.canDeleteTree(tree.created_by); return `
Tree #${tree.id}${ishitaIndex.has(tree.id) ? ` (Ishita No. ${ishitaIndex.get(tree.id)})` : ''}
${canEdit ? `` : ''} ${canDelete ? `` : ''}
${tree.scientific_name || tree.common_name || tree.local_name || 'Unnamed'} ${tree.location_name ? `
Location: ${this.escapeHtml(tree.location_name)}` : ''}
${tree.latitude.toFixed(4)}, ${tree.longitude.toFixed(4)} ${tree.tree_code ? `
Code: ${tree.tree_code}` : ''}
${new Date(tree.created_at).toLocaleDateString()}
By: ${tree.created_by || 'Unknown'}
`; }).join(''); } escapeHtml(text) { if (typeof text !== 'string') return text; return text .replace(/&/g, '&') .replace(//g, '>') .replace(/"/g, '"') .replace(/'/g, '''); } showLoadingState(containerId, message = 'Loading...') { const container = document.getElementById(containerId); if (!container) return; container.innerHTML = `
${message}
`; } showErrorState(containerId, message = 'Error loading content') { const container = document.getElementById(containerId); if (!container) return; container.innerHTML = `
${message}
`; } updateLocationButtonState(isGetting) { const locationBtn = document.getElementById('getLocation'); if (!locationBtn) return; locationBtn.textContent = isGetting ? 'Getting...' : 'Get GPS Location'; locationBtn.disabled = isGetting; } highlightAutoFilledField(fieldId) { const input = document.getElementById(fieldId); if (input && !input.value.trim()) { input.style.backgroundColor = '#f0f9ff'; setTimeout(() => { input.style.backgroundColor = ''; }, 2000); } } createFileInput(accept, capture = false) { const input = document.createElement('input'); input.type = 'file'; input.accept = accept; if (capture) { input.capture = 'environment'; } return input; } confirmDeletion(treeId) { return confirm(`Are you sure you want to delete Tree #${treeId}? This action cannot be undone.`); } scrollToTop() { window.scrollTo({ top: 0, behavior: 'smooth' }); } focusFirstError() { const firstError = document.querySelector('.form-input.error, .message.error'); if (firstError) { firstError.scrollIntoView({ behavior: 'smooth', block: 'center' }); if (firstError.focus) { firstError.focus(); } } } addFieldError(fieldId, message) { const field = document.getElementById(fieldId); if (!field) return; field.classList.add('error'); // Remove existing error message const existingError = field.parentNode.querySelector('.field-error'); if (existingError) { existingError.remove(); } // Add error message const errorEl = document.createElement('div'); errorEl.className = 'field-error'; errorEl.textContent = message; field.parentNode.appendChild(errorEl); } clearFieldErrors() { document.querySelectorAll('.form-input.error').forEach(field => { field.classList.remove('error'); }); document.querySelectorAll('.field-error').forEach(error => { error.remove(); }); } showUploadProgress(filename, progress) { // This could be expanded for actual progress tracking console.log(`Uploading ${filename}: ${progress}%`); } setupDemoUserUI() { if (!this.authManager.isDemoUser()) { return; // Not a demo user, no changes needed } // Add demo notice at the top of the form this.addDemoNotice(); // Disable submit button and update its appearance this.disableSubmitButtonForDemo(); // Show welcome button for demo users this.showWelcomeButton(); } addDemoNotice() { const formCard = document.querySelector('.tt-card .tt-card-content'); if (!formCard) return; const demoNotice = document.createElement('div'); demoNotice.className = 'demo-notice'; demoNotice.innerHTML = `
i
Demo Mode - You're exploring TreeTrack! Feel free to test all features and fill out the form. Note: Data won't be permanently saved in this demo environment.
`; // Insert before the form const form = document.getElementById('treeForm'); if (form) { formCard.insertBefore(demoNotice, form); } } disableSubmitButtonForDemo() { const submitBtn = document.querySelector('button[type="submit"]'); if (!submitBtn) return; // Update button text and styling submitBtn.textContent = 'Try TreeTrack - Demo Mode'; submitBtn.classList.add('tt-btn-demo-disabled'); submitBtn.disabled = true; // Add tooltip on hover submitBtn.title = 'This is a demo environment - data won\'t be permanently saved'; // Override the form submission to show demo message submitBtn.setAttribute('data-demo-disabled', 'true'); } isDemoButtonDisabled() { const submitBtn = document.querySelector('button[type="submit"]'); return submitBtn && submitBtn.getAttribute('data-demo-disabled') === 'true'; } showWelcomeButton() { const welcomeBtn = document.getElementById('welcomeBtn'); if (welcomeBtn) { welcomeBtn.style.display = 'inline-flex'; } } }