/** * 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(); } 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}%`); } }