document.addEventListener('DOMContentLoaded', function() { const mainContent = document.getElementById('main-content'); const appName = document.querySelector('.navbar-brand').textContent; // --- Helper Functions --- function setPageTitle(title) { document.title = `${appName} - ${title}`; } function showLoading() { mainContent.innerHTML = '
Loading...
'; } // Function to fetch content and update the main view function fetchContent(path, pushState = true) { showLoading(); // Determine the API endpoint based on the path let apiPath = ''; let pageTitle = ''; let routePath = path; if (path === '/' || path === '/overview') { apiPath = '/api/items'; pageTitle = 'Overview'; routePath = '/'; } else if (path === '/categories') { apiPath = '/api/categories'; pageTitle = 'Categories'; } else if (path === '/parts') { apiPath = '/api/parts'; pageTitle = 'Parts'; } else { // Fallback apiPath = '/api/items'; pageTitle = 'Overview'; routePath = '/'; } fetch(apiPath) .then(response => { if (!response.ok) { throw new Error(`HTTP error! status: ${response.status}`); } return response.text(); }) .then(html => { mainContent.innerHTML = html; if (pushState) { history.pushState({ html: html, path: routePath }, '', routePath); } setPageTitle(pageTitle); updateActiveLink(routePath); // Re-initialize any scripts specific to the loaded content initPageSpecificScripts(); }) .catch(error => { console.error('Error fetching content:', error); mainContent.innerHTML = `
${error.message}
`; }); } // Update active class on sidebar links function updateActiveLink(path) { document.querySelectorAll('.nav-link[data-route]').forEach(link => { link.classList.remove('active'); if (link.getAttribute('data-route') === path) { link.classList.add('active'); } }); } // Handle browser back/forward buttons window.onpopstate = function(event) { if (event.state && event.state.html) { mainContent.innerHTML = event.state.html; setPageTitle(event.state.path.substring(1) || 'Home'); updateActiveLink(event.state.path); initPageSpecificScripts(); } else { // If no state, force a full load of the current path fetchContent(window.location.pathname, false); } }; // --- Event Listeners --- // Navigation links (Navbar and Sidebar) document.body.addEventListener('click', function(e) { const link = e.target.closest('a[data-route]'); if (link) { e.preventDefault(); const route = link.getAttribute('data-route'); fetchContent(route); } }); // Initial page load const initialPath = window.location.pathname === '/' ? '/items' : window.location.pathname; fetchContent(initialPath, false); // Page-specific scripts (e.g., form submissions) function initPageSpecificScripts() { // Overview page: filter form const filterForm = document.getElementById('filterForm'); if (filterForm) { filterForm.addEventListener('submit', handleFilter); } // Overview page: edit/delete buttons document.querySelectorAll('.edit-btn').forEach(btn => { btn.addEventListener('click', handleEditItem); }); document.querySelectorAll('.delete-btn').forEach(btn => { btn.addEventListener('click', handleDeleteItem); }); // Parts page: add item form const addItemForm = document.getElementById('addItemForm'); if (addItemForm) { addItemForm.addEventListener('submit', handleAddItem); } // Categories page: add category form const addCategoryForm = document.getElementById('addCategoryForm'); if (addCategoryForm) { addCategoryForm.addEventListener('submit', handleAddCategory); } // Overview page: save edit item const saveEditItemBtn = document.getElementById('saveEditItemBtn'); if (saveEditItemBtn) { saveEditItemBtn.addEventListener('click', handleSaveEditItem); } // Categories page: save edit category const saveEditBtn = document.getElementById('saveEditCategoryBtn'); if (saveEditBtn) { saveEditBtn.addEventListener('click', handleSaveEditCategory); } // Categories page: edit/delete buttons document.querySelectorAll('.edit-category-btn').forEach(btn => { btn.addEventListener('click', handleEditCategory); }); document.querySelectorAll('.delete-category-btn').forEach(btn => { btn.addEventListener('click', handleDeleteCategory); }); } // Handler functions function handleAddItem(e) { e.preventDefault(); const formData = new FormData(this); fetch('/api/items', { method: 'POST', body: formData }) .then(response => response.json()) .then(data => { if (data.success) { fetchContent('/', false); // Go to overview to see the new part } else { alert(data.error || 'Error adding part'); } }) .catch(error => { console.error('Error:', error); alert('Error adding part'); }); } function handleAddCategoryFromItem() { const name = document.getElementById('new_category_name').value.trim(); if (!name) return; const formData = new FormData(); formData.append('category_name', name); fetch('/api/categories', { method: 'POST', body: formData }) .then(response => response.json()) .then(data => { if (data.success) { const select = document.getElementById('category_id'); const option = new Option(name, data.id); select.appendChild(option); select.value = data.id; document.getElementById('new_category_name').value = ''; document.getElementById('addCategoryForm').classList.add('hidden'); } else { alert(data.error || 'Error adding category'); } }) .catch(error => { console.error('Error:', error); alert('Error adding category'); }); } function handleFilter(e) { e.preventDefault(); const search = document.getElementById('search').value; const categoryId = document.getElementById('category_filter').value; let url = '/api/items?'; if (search) url += 'search=' + encodeURIComponent(search) + '&'; if (categoryId) url += 'category_id=' + categoryId; fetch(url) .then(response => response.text()) .then(html => { document.getElementById('itemsList').outerHTML = html.match(/]*id="itemsList"[^>]*>[\s\S]*?<\/ul>/)[0]; initPageSpecificScripts(); // Re-init for new buttons }) .catch(error => { console.error('Error:', error); }); } function handleEditItem() { const id = this.getAttribute('data-id'); // Fetch current item data fetch('/api/items/' + id) .then(response => response.json()) .then(data => { if (data) { document.getElementById('edit_item_id').value = data.id; document.getElementById('edit_item_name').value = data.name; document.getElementById('edit_item_description').value = data.description || ''; document.getElementById('edit_location').value = data.location || ''; // Populate category select const categorySelect = document.getElementById('edit_item_category_id'); categorySelect.innerHTML = ''; fetch('/api/categories/list') .then(resp => resp.json()) .then(categories => { categories.forEach(cat => { const option = document.createElement('option'); option.value = cat.id; option.textContent = cat.path; categorySelect.appendChild(option); }); categorySelect.value = data.category_id || ''; const modal = new bootstrap.Modal(document.getElementById('editItemModal')); modal.show(); }); } else { alert('Part not found'); } }) .catch(error => { console.error('Error:', error); alert('Error fetching part'); }); } function handleDeleteItem() { const id = this.getAttribute('data-id'); if (confirm(window.translations.deletePartConfirm)) { fetch('/api/items/' + id, { method: 'DELETE' }) .then(response => response.json()) .then(data => { if (data.success) { this.closest('li').remove(); } else { alert(data.error || 'Error deleting part'); } }) .catch(error => { console.error('Error:', error); alert('Error deleting part'); }); } } function handleAddCategory() { const formData = new FormData(this); fetch('/api/categories', { method: 'POST', body: formData }) .then(response => response.json()) .then(data => { if (data.success) { fetchContent('/categories', false); } else { alert(data.error || 'Error adding category'); } }) .catch(error => { console.error('Error:', error); alert('Error adding category'); }); } function handleSaveEditItem() { const id = document.getElementById('edit_item_id').value; const name = document.getElementById('edit_item_name').value.trim(); const description = document.getElementById('edit_item_description').value; const categoryId = document.getElementById('edit_item_category_id').value; const location = document.getElementById('edit_location').value.trim(); const imageFile = document.getElementById('edit_image').files[0]; if (!name) { alert('Part name is required'); return; } const formData = new FormData(); formData.append('item_name', name); formData.append('item_description', description); formData.append('category_id', categoryId || ''); formData.append('location', location); if (imageFile) { formData.append('image', imageFile); } fetch('/api/items/' + id, { method: 'PUT', body: formData }) .then(response => response.json()) .then(data => { if (data.success) { bootstrap.Modal.getInstance(document.getElementById('editItemModal')).hide(); fetchContent('/', false); // Reload overview } else { alert(data.error || 'Error updating part'); } }) .catch(error => { console.error('Error:', error); alert('Error updating part'); }); } function handleSaveEditCategory() { const id = document.getElementById('edit_category_id').value; const name = document.getElementById('edit_category_name').value.trim(); const parentId = document.getElementById('edit_parent_category_id').value; if (!name) { alert('Category name is required'); return; } const data = { category_name: name, parent_category_id: parentId || null }; fetch('/api/categories/' + id, { method: 'PUT', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(data) }) .then(response => response.json()) .then(data => { if (data.success) { bootstrap.Modal.getInstance(document.getElementById('editCategoryModal')).hide(); fetchContent('/categories', false); } else { alert(data.error || 'Error updating category'); } }) .catch(error => { console.error('Error:', error); alert('Error updating category'); }); } function handleEditCategory() { const id = this.getAttribute('data-id'); // Fetch current category data fetch('/api/categories/' + id) .then(response => { if (!response.ok) { throw new Error('HTTP ' + response.status + ': ' + response.statusText); } return response.json(); }) .then(data => { if (data && !data.error) { document.getElementById('edit_category_id').value = data.id; document.getElementById('edit_category_name').value = data.name; // Populate parent select, excluding current category const parentSelect = document.getElementById('edit_parent_category_id'); parentSelect.innerHTML = ''; fetch('/api/categories/list') .then(resp => resp.json()) .then(categories => { categories.forEach(cat => { if (cat.id != data.id) { const option = document.createElement('option'); option.value = cat.id; option.textContent = cat.path; parentSelect.appendChild(option); } }); parentSelect.value = data.parent_id || ''; const modal = new bootstrap.Modal(document.getElementById('editCategoryModal')); modal.show(); }); } else { alert(data.error || 'Category not found'); } }) .catch(error => { console.error('Error:', error); alert('Error fetching category: ' + error.message); }); } function handleDeleteCategory() { const id = this.getAttribute('data-id'); if (confirm(window.translations.deleteCategoryConfirm)) { fetch('/api/categories/' + id, { method: 'DELETE' }) .then(response => response.json()) .then(data => { if (data.success) { this.closest('li').remove(); } else { alert(data.error || 'Error deleting category'); } }) .catch(error => { console.error('Error:', error); alert('Error deleting category'); }); } } });