TreeTrack / static /js /tree-track-app.js
RoyAalekh's picture
Major refactoring: Modular architecture implementation (v5.0.0)
afc0068
raw
history blame
8.52 kB
/**
* TreeTrack Enhanced Application - Modular Architecture
* Professional Field Research Tool with Authentication
*/
// Import all modules
import { AuthManager } from './modules/auth-manager.js';
import { ApiClient } from './modules/api-client.js';
import { UIManager } from './modules/ui-manager.js';
import { FormManager } from './modules/form-manager.js';
import { AutoCompleteManager } from './modules/autocomplete-manager.js';
import { MediaManager } from './modules/media-manager.js';
export class TreeTrackApp {
constructor() {
// Initialize core managers
this.authManager = new AuthManager();
this.apiClient = new ApiClient(this.authManager);
this.uiManager = new UIManager(this.authManager);
this.formManager = new FormManager(this.apiClient, this.uiManager);
this.autoCompleteManager = new AutoCompleteManager(this.apiClient, this.uiManager);
this.mediaManager = new MediaManager(this.formManager, this.uiManager);
this.init();
}
async init() {
try {
// Check authentication first
if (!await this.authManager.checkAuthentication()) {
window.location.href = '/login';
return;
}
// Initialize all managers
await this.initializeManagers();
// Setup event listeners
this.setupEventListeners();
// Load initial data
await this.loadInitialData();
} catch (error) {
console.error('Error initializing TreeTrack app:', error);
this.uiManager.showMessage('Error initializing application: ' + error.message, 'error');
}
}
async initializeManagers() {
// Initialize UI first
this.uiManager.initialize();
// Initialize form components
await this.formManager.initialize();
// Initialize media handling
this.mediaManager.initialize();
// Initialize autocomplete (with delay to ensure DOM is ready)
setTimeout(async () => {
await this.autoCompleteManager.initialize();
}, 100);
}
setupEventListeners() {
// Form submission
const form = document.getElementById('treeForm');
if (form) {
form.addEventListener('submit', (e) => this.handleFormSubmit(e));
}
// Reset form
const resetBtn = document.getElementById('resetForm');
if (resetBtn) {
resetBtn.addEventListener('click', () => this.handleFormReset());
}
// GPS location
const locationBtn = document.getElementById('getLocation');
if (locationBtn) {
locationBtn.addEventListener('click', () => this.mediaManager.getCurrentLocation());
}
// Logout functionality
const logoutBtn = document.getElementById('logoutBtn');
if (logoutBtn) {
logoutBtn.addEventListener('click', () => this.authManager.logout());
}
// Tree actions (edit/delete)
document.addEventListener('click', (e) => {
if (e.target.matches('.edit-tree')) {
const treeId = e.target.dataset.treeId;
if (treeId) {
this.handleEditTree(parseInt(treeId));
}
}
if (e.target.matches('.delete-tree')) {
const treeId = e.target.dataset.treeId;
if (treeId) {
this.handleDeleteTree(parseInt(treeId));
}
}
if (e.target.matches('#cancelEdit')) {
this.handleCancelEdit();
}
});
}
async loadInitialData() {
try {
await this.loadTrees();
} catch (error) {
console.error('Error loading initial data:', error);
}
}
async handleFormSubmit(e) {
e.preventDefault();
try {
// Clear any previous field errors
this.uiManager.clearFieldErrors();
// Validate form
this.formManager.validateForm();
// Get form data
const treeData = this.formManager.getFormData();
// Save or update tree
let result;
if (this.formManager.isInEditMode()) {
result = await this.apiClient.updateTree(this.formManager.getCurrentEditId(), treeData);
this.uiManager.showMessage(`Tree #${result.id} updated successfully!`, 'success');
this.handleCancelEdit();
} else {
result = await this.apiClient.saveTree(treeData);
this.uiManager.showMessage(
`Tree successfully added! Tree ID: ${result.id}. The form has been cleared for your next entry.`,
'success'
);
this.formManager.resetForm(true);
}
// Refresh tree list
await this.loadTrees();
this.uiManager.scrollToTop();
} catch (error) {
console.error('Error submitting form:', error);
this.uiManager.showMessage('Error saving tree: ' + error.message, 'error');
this.uiManager.focusFirstError();
}
}
handleFormReset() {
this.formManager.resetForm();
this.uiManager.clearFieldErrors();
}
async handleEditTree(treeId) {
try {
this.uiManager.showLoadingState('message', 'Loading tree data...');
const tree = await this.apiClient.loadTree(treeId);
if (!tree) return;
this.formManager.populateForm(tree);
this.formManager.setEditMode(treeId);
this.uiManager.showMessage(`Loaded tree #${treeId} for editing. Make changes and save.`, 'success');
this.uiManager.scrollToTop();
} catch (error) {
console.error('Error loading tree for edit:', error);
this.uiManager.showMessage('Error loading tree data: ' + error.message, 'error');
}
}
async handleDeleteTree(treeId) {
if (!this.uiManager.confirmDeletion(treeId)) {
return;
}
try {
await this.apiClient.deleteTree(treeId);
this.uiManager.showMessage(`Tree #${treeId} deleted successfully.`, 'success');
await this.loadTrees();
} catch (error) {
console.error('Error deleting tree:', error);
this.uiManager.showMessage('Error deleting tree: ' + error.message, 'error');
}
}
handleCancelEdit() {
this.formManager.resetForm(true);
this.formManager.exitEditMode();
this.uiManager.clearFieldErrors();
this.uiManager.showMessage('Edit cancelled. Form cleared.', 'success');
}
async loadTrees() {
try {
this.uiManager.showLoadingState('treeList', 'Loading trees...');
const trees = await this.apiClient.loadTrees();
this.uiManager.renderTreeList(trees);
} catch (error) {
console.error('Error loading trees:', error);
this.uiManager.showErrorState('treeList', 'Error loading trees');
}
}
// Utility methods for external access (maintaining compatibility)
showMessage(message, type) {
this.uiManager.showMessage(message, type);
}
// Global functions for backward compatibility (deprecated - use event delegation instead)
editTree(treeId) {
console.warn('Global editTree function is deprecated. Use data attributes instead.');
this.handleEditTree(treeId);
}
deleteTree(treeId) {
console.warn('Global deleteTree function is deprecated. Use data attributes instead.');
this.handleDeleteTree(treeId);
}
capturePhoto(category) {
console.warn('Global capturePhoto function is deprecated. Use data attributes instead.');
this.mediaManager.capturePhoto(category);
}
}
// Initialize the app when the page loads
let app;
document.addEventListener('DOMContentLoaded', () => {
app = new TreeTrackApp();
});
// Expose app globally for debugging and backward compatibility
window.TreeTrackApp = TreeTrackApp;
window.app = app;