Fix JavaScript tree reload after CRUD operations
- Add reloadTree() function to centralize tree updates - Fix handleSaveEditItem to reload tree after item updates - Fix handleAddItem to reload tree after adding items - Fix handleDeleteItem to reload tree after item deletion - Fix all category functions to reload tree after operations - Replace broken Bootstrap.Modal.getInstance() with direct DOM manipulation - Fix JavaScript syntax errors and missing braces - Ensure tree updates automatically without manual page reload Resolves issue where tree navigation was not updating after item modifications.
This commit is contained in:
parent
9f9617ca45
commit
d5ac2a21b3
142
public/js/app.js
142
public/js/app.js
@ -4,21 +4,28 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||||||
|
|
||||||
// Global function for editing item from tree
|
// Global function for editing item from tree
|
||||||
window.editItem = function(id) {
|
window.editItem = function(id) {
|
||||||
// Create a mock button to reuse handleEditItem
|
|
||||||
const mockBtn = { getAttribute: (attr) => attr === 'data-id' ? id : null };
|
const mockBtn = { getAttribute: (attr) => attr === 'data-id' ? id : null };
|
||||||
handleEditItem.call(mockBtn);
|
handleEditItem.call(mockBtn);
|
||||||
};
|
};
|
||||||
|
|
||||||
// Function to toggle category children
|
// Function to toggle tree nodes
|
||||||
window.toggleCategory = function(span) {
|
window.toggleNode = function(element) {
|
||||||
const li = span.parentElement;
|
const li = element.closest('li');
|
||||||
const ul = li.querySelector('ul'); // First ul is children
|
const childUl = li.querySelector(':scope > ul');
|
||||||
if (ul) {
|
if (childUl) {
|
||||||
ul.style.display = ul.style.display === 'none' ? 'block' : 'none';
|
const isHidden = childUl.style.display === 'none';
|
||||||
|
childUl.style.display = isHidden ? 'block' : 'none';
|
||||||
|
const icon = element.querySelector('i');
|
||||||
|
if (icon) {
|
||||||
|
if (isHidden) {
|
||||||
|
icon.className = 'bi bi-folder-fill text-warning';
|
||||||
|
} else {
|
||||||
|
icon.className = 'bi bi-folder text-warning';
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// --- Helper Functions ---
|
|
||||||
function setPageTitle(title) {
|
function setPageTitle(title) {
|
||||||
document.title = `${appName} - ${title}`;
|
document.title = `${appName} - ${title}`;
|
||||||
}
|
}
|
||||||
@ -27,11 +34,24 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||||||
mainContent.innerHTML = '<div class="text-center p-5"><div class="spinner-border text-primary" role="status"><span class="visually-hidden">Loading...</span></div></div>';
|
mainContent.innerHTML = '<div class="text-center p-5"><div class="spinner-border text-primary" role="status"><span class="visually-hidden">Loading...</span></div></div>';
|
||||||
}
|
}
|
||||||
|
|
||||||
// Function to fetch content and update the main view
|
function reloadTree() {
|
||||||
|
fetch('/api/tree')
|
||||||
|
.then(response => response.text())
|
||||||
|
.then(html => {
|
||||||
|
const sidebarTree = document.getElementById('sidebar-tree');
|
||||||
|
if (sidebarTree) {
|
||||||
|
sidebarTree.innerHTML = html;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
console.error('Error loading tree:', error);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
function fetchContent(path, pushState = true) {
|
function fetchContent(path, pushState = true) {
|
||||||
|
console.log('fetchContent called with path:', path);
|
||||||
showLoading();
|
showLoading();
|
||||||
|
|
||||||
// Determine the API endpoint based on the path
|
|
||||||
let apiPath = '';
|
let apiPath = '';
|
||||||
let pageTitle = '';
|
let pageTitle = '';
|
||||||
let routePath = path;
|
let routePath = path;
|
||||||
@ -40,6 +60,10 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||||||
apiPath = '/api/items';
|
apiPath = '/api/items';
|
||||||
pageTitle = 'Overview';
|
pageTitle = 'Overview';
|
||||||
routePath = '/';
|
routePath = '/';
|
||||||
|
} else if (path.startsWith('/items')) {
|
||||||
|
apiPath = '/api/items';
|
||||||
|
pageTitle = 'Overview';
|
||||||
|
routePath = '/';
|
||||||
} else if (path.startsWith('/categories')) {
|
} else if (path.startsWith('/categories')) {
|
||||||
if (path.includes('?category=')) {
|
if (path.includes('?category=')) {
|
||||||
const categoryId = path.split('?category=')[1];
|
const categoryId = path.split('?category=')[1];
|
||||||
@ -59,7 +83,6 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||||||
pageTitle = 'Parts';
|
pageTitle = 'Parts';
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Fallback
|
|
||||||
apiPath = '/api/items';
|
apiPath = '/api/items';
|
||||||
pageTitle = 'Overview';
|
pageTitle = 'Overview';
|
||||||
routePath = '/';
|
routePath = '/';
|
||||||
@ -79,7 +102,6 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||||||
}
|
}
|
||||||
setPageTitle(pageTitle);
|
setPageTitle(pageTitle);
|
||||||
updateActiveLink(routePath);
|
updateActiveLink(routePath);
|
||||||
// Re-initialize any scripts specific to the loaded content
|
|
||||||
initPageSpecificScripts();
|
initPageSpecificScripts();
|
||||||
})
|
})
|
||||||
.catch(error => {
|
.catch(error => {
|
||||||
@ -88,7 +110,6 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update active class on sidebar links
|
|
||||||
function updateActiveLink(path) {
|
function updateActiveLink(path) {
|
||||||
document.querySelectorAll('.nav-link[data-route]').forEach(link => {
|
document.querySelectorAll('.nav-link[data-route]').forEach(link => {
|
||||||
link.classList.remove('active');
|
link.classList.remove('active');
|
||||||
@ -98,7 +119,6 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle browser back/forward buttons
|
|
||||||
window.onpopstate = function(event) {
|
window.onpopstate = function(event) {
|
||||||
if (event.state && event.state.html) {
|
if (event.state && event.state.html) {
|
||||||
mainContent.innerHTML = event.state.html;
|
mainContent.innerHTML = event.state.html;
|
||||||
@ -106,14 +126,10 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||||||
updateActiveLink(event.state.path);
|
updateActiveLink(event.state.path);
|
||||||
initPageSpecificScripts();
|
initPageSpecificScripts();
|
||||||
} else {
|
} else {
|
||||||
// If no state, force a full load of the current path
|
|
||||||
fetchContent(window.location.pathname, false);
|
fetchContent(window.location.pathname, false);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// --- Event Listeners ---
|
|
||||||
|
|
||||||
// Navigation links (Navbar and Sidebar)
|
|
||||||
document.body.addEventListener('click', function(e) {
|
document.body.addEventListener('click', function(e) {
|
||||||
const link = e.target.closest('a[data-route]');
|
const link = e.target.closest('a[data-route]');
|
||||||
if (link) {
|
if (link) {
|
||||||
@ -140,7 +156,6 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||||||
const initialPath = window.location.pathname === '/' ? '/items' : window.location.pathname;
|
const initialPath = window.location.pathname === '/' ? '/items' : window.location.pathname;
|
||||||
fetchContent(initialPath, false);
|
fetchContent(initialPath, false);
|
||||||
|
|
||||||
// Page-specific scripts (e.g., form submissions)
|
|
||||||
function initPageSpecificScripts() {
|
function initPageSpecificScripts() {
|
||||||
// Overview page: filter form
|
// Overview page: filter form
|
||||||
const filterForm = document.getElementById('filterForm');
|
const filterForm = document.getElementById('filterForm');
|
||||||
@ -175,9 +190,9 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Categories page: save edit category
|
// Categories page: save edit category
|
||||||
const saveEditBtn = document.getElementById('saveEditCategoryBtn');
|
const saveEditCategoryBtn = document.getElementById('saveEditCategoryBtn');
|
||||||
if (saveEditBtn) {
|
if (saveEditCategoryBtn) {
|
||||||
saveEditBtn.addEventListener('click', handleSaveEditCategory);
|
saveEditCategoryBtn.addEventListener('click', handleSaveEditCategory);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Categories page: edit/delete buttons
|
// Categories page: edit/delete buttons
|
||||||
@ -201,6 +216,7 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||||||
.then(data => {
|
.then(data => {
|
||||||
if (data.success) {
|
if (data.success) {
|
||||||
fetchContent('/', false);
|
fetchContent('/', false);
|
||||||
|
reloadTree(); // Reload tree after deletion
|
||||||
} else {
|
} else {
|
||||||
alert(data.error || 'Error deleting item');
|
alert(data.error || 'Error deleting item');
|
||||||
}
|
}
|
||||||
@ -225,7 +241,8 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||||||
.then(response => response.json())
|
.then(response => response.json())
|
||||||
.then(data => {
|
.then(data => {
|
||||||
if (data.success) {
|
if (data.success) {
|
||||||
fetchContent('/', false); // Go to overview to see the new part
|
fetchContent('/', false);
|
||||||
|
reloadTree(); // Reload tree after adding
|
||||||
} else {
|
} else {
|
||||||
alert(data.error || 'Error adding part');
|
alert(data.error || 'Error adding part');
|
||||||
}
|
}
|
||||||
@ -236,34 +253,6 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
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) {
|
function handleFilter(e) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
const search = document.getElementById('search').value;
|
const search = document.getElementById('search').value;
|
||||||
@ -275,7 +264,7 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||||||
.then(response => response.text())
|
.then(response => response.text())
|
||||||
.then(html => {
|
.then(html => {
|
||||||
document.getElementById('itemsList').outerHTML = html.match(/<ul[^>]*id="itemsList"[^>]*>[\s\S]*?<\/ul>/)[0];
|
document.getElementById('itemsList').outerHTML = html.match(/<ul[^>]*id="itemsList"[^>]*>[\s\S]*?<\/ul>/)[0];
|
||||||
initPageSpecificScripts(); // Re-init for new buttons
|
initPageSpecificScripts();
|
||||||
})
|
})
|
||||||
.catch(error => {
|
.catch(error => {
|
||||||
console.error('Error:', error);
|
console.error('Error:', error);
|
||||||
@ -284,7 +273,6 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||||||
|
|
||||||
function handleEditItem() {
|
function handleEditItem() {
|
||||||
const id = this.getAttribute('data-id');
|
const id = this.getAttribute('data-id');
|
||||||
// Fetch current item data
|
|
||||||
fetch('/api/items/' + id)
|
fetch('/api/items/' + id)
|
||||||
.then(response => response.json())
|
.then(response => response.json())
|
||||||
.then(data => {
|
.then(data => {
|
||||||
@ -299,7 +287,6 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||||||
} else {
|
} else {
|
||||||
currentImageDiv.innerHTML = '<p>Geen afbeelding</p>';
|
currentImageDiv.innerHTML = '<p>Geen afbeelding</p>';
|
||||||
}
|
}
|
||||||
// Populate category select
|
|
||||||
const categorySelect = document.getElementById('edit_item_category_id');
|
const categorySelect = document.getElementById('edit_item_category_id');
|
||||||
categorySelect.innerHTML = '<option value="">-- Select Category --</option>';
|
categorySelect.innerHTML = '<option value="">-- Select Category --</option>';
|
||||||
fetch('/api/categories/list')
|
fetch('/api/categories/list')
|
||||||
@ -313,6 +300,10 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||||||
});
|
});
|
||||||
categorySelect.value = data.category_id || '';
|
categorySelect.value = data.category_id || '';
|
||||||
const modal = new bootstrap.Modal(document.getElementById('editItemModal'));
|
const modal = new bootstrap.Modal(document.getElementById('editItemModal'));
|
||||||
|
const saveEditItemBtn = document.getElementById('saveEditItemBtn');
|
||||||
|
if (saveEditItemBtn) {
|
||||||
|
saveEditItemBtn.addEventListener('click', handleSaveEditItem);
|
||||||
|
}
|
||||||
modal.show();
|
modal.show();
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
@ -335,6 +326,7 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||||||
.then(data => {
|
.then(data => {
|
||||||
if (data.success) {
|
if (data.success) {
|
||||||
this.closest('li').remove();
|
this.closest('li').remove();
|
||||||
|
reloadTree(); // Reload tree after deletion
|
||||||
} else {
|
} else {
|
||||||
alert(data.error || 'Error deleting part');
|
alert(data.error || 'Error deleting part');
|
||||||
}
|
}
|
||||||
@ -356,6 +348,7 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||||||
.then(data => {
|
.then(data => {
|
||||||
if (data.success) {
|
if (data.success) {
|
||||||
fetchContent('/categories', false);
|
fetchContent('/categories', false);
|
||||||
|
reloadTree(); // Reload tree after adding
|
||||||
} else {
|
} else {
|
||||||
alert(data.error || 'Error adding category');
|
alert(data.error || 'Error adding category');
|
||||||
}
|
}
|
||||||
@ -395,15 +388,25 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||||||
.then(response => response.json())
|
.then(response => response.json())
|
||||||
.then(data => {
|
.then(data => {
|
||||||
if (data.success) {
|
if (data.success) {
|
||||||
bootstrap.Modal.getInstance(document.getElementById('editItemModal')).hide();
|
// Hide modal
|
||||||
fetchContent('/', false); // Reload overview
|
const modalElement = document.getElementById('editItemModal');
|
||||||
|
if (modalElement) {
|
||||||
|
modalElement.classList.remove('show', 'showing');
|
||||||
|
modalElement.style.display = 'none';
|
||||||
|
modalElement.setAttribute('aria-hidden', 'true');
|
||||||
|
document.body.classList.remove('modal-open');
|
||||||
|
const backdrops = document.querySelectorAll(' .modal-backdrop');
|
||||||
|
backdrops.forEach(backdrop => backdrop.remove());
|
||||||
|
}
|
||||||
|
fetchContent('/', false);
|
||||||
|
reloadTree(); // Reload tree after updating
|
||||||
} else {
|
} else {
|
||||||
alert(data.error || 'Error updating part');
|
alert(data.error || 'Error updating part');
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.catch(error => {
|
.catch(error => {
|
||||||
console.error('Error:', error);
|
console.error('Update error:', error);
|
||||||
alert('Error updating part');
|
alert('Error updating part: ' + error.message);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -432,21 +435,30 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||||||
.then(response => response.json())
|
.then(response => response.json())
|
||||||
.then(data => {
|
.then(data => {
|
||||||
if (data.success) {
|
if (data.success) {
|
||||||
bootstrap.Modal.getInstance(document.getElementById('editCategoryModal')).hide();
|
// Hide modal
|
||||||
|
const modalElement = document.getElementById('editCategoryModal');
|
||||||
|
if (modalElement) {
|
||||||
|
modalElement.classList.remove('show', 'showing');
|
||||||
|
modalElement.style.display = 'none';
|
||||||
|
modalElement.setAttribute('aria-hidden', 'true');
|
||||||
|
document.body.classList.remove('modal-open');
|
||||||
|
const backdrops = document.querySelectorAll(' .modal-backdrop');
|
||||||
|
backdrops.forEach(backdrop => backdrop.remove());
|
||||||
|
}
|
||||||
fetchContent('/categories', false);
|
fetchContent('/categories', false);
|
||||||
|
reloadTree(); // Reload tree after updating
|
||||||
} else {
|
} else {
|
||||||
alert(data.error || 'Error updating category');
|
alert(data.error || 'Error updating category');
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.catch(error => {
|
.catch(error => {
|
||||||
console.error('Error:', error);
|
console.error('Update error:', error);
|
||||||
alert('Error updating category');
|
alert('Error updating category: ' + error.message);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleEditCategory() {
|
function handleEditCategory() {
|
||||||
const id = this.getAttribute('data-id');
|
const id = this.getAttribute('data-id');
|
||||||
// Fetch current category data
|
|
||||||
fetch('/api/categories/' + id)
|
fetch('/api/categories/' + id)
|
||||||
.then(response => {
|
.then(response => {
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
@ -458,7 +470,6 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||||||
if (data && !data.error) {
|
if (data && !data.error) {
|
||||||
document.getElementById('edit_category_id').value = data.id;
|
document.getElementById('edit_category_id').value = data.id;
|
||||||
document.getElementById('edit_category_name').value = data.name;
|
document.getElementById('edit_category_name').value = data.name;
|
||||||
// Populate parent select, excluding current category
|
|
||||||
const parentSelect = document.getElementById('edit_parent_category_id');
|
const parentSelect = document.getElementById('edit_parent_category_id');
|
||||||
parentSelect.innerHTML = '<option value="">-- No Parent --</option>';
|
parentSelect.innerHTML = '<option value="">-- No Parent --</option>';
|
||||||
fetch('/api/categories/list')
|
fetch('/api/categories/list')
|
||||||
@ -474,6 +485,10 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||||||
});
|
});
|
||||||
parentSelect.value = data.parent_id || '';
|
parentSelect.value = data.parent_id || '';
|
||||||
const modal = new bootstrap.Modal(document.getElementById('editCategoryModal'));
|
const modal = new bootstrap.Modal(document.getElementById('editCategoryModal'));
|
||||||
|
const saveEditCategoryBtn = document.getElementById('saveEditCategoryBtn');
|
||||||
|
if (saveEditCategoryBtn) {
|
||||||
|
saveEditCategoryBtn.addEventListener('click', handleSaveEditCategory);
|
||||||
|
}
|
||||||
modal.show();
|
modal.show();
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
@ -496,6 +511,7 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||||||
.then(data => {
|
.then(data => {
|
||||||
if (data.success) {
|
if (data.success) {
|
||||||
this.closest('li').remove();
|
this.closest('li').remove();
|
||||||
|
reloadTree(); // Reload tree after deletion
|
||||||
} else {
|
} else {
|
||||||
alert(data.error || 'Error deleting category');
|
alert(data.error || 'Error deleting category');
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user