Add new fields to items: id_code, image, location; implement QR code generation and printing; update translations and UI
This commit is contained in:
@@ -1,13 +1,13 @@
|
||||
<?php
|
||||
<?php
|
||||
|
||||
$autoloadPath = __DIR__ . '/../vendor/autoload.php';
|
||||
if (!file_exists($autoloadPath)) {
|
||||
http_response_code(500);
|
||||
die("FATAL ERROR: Composer autoloader not found. Please run 'composer install' in the project root.");
|
||||
}
|
||||
require $autoloadPath;
|
||||
require $autoloadPath;
|
||||
|
||||
require __DIR__ . '/../config.php';
|
||||
require __DIR__ . '/../config.php';
|
||||
|
||||
use App\Router;
|
||||
use App\Controllers\ItemController;
|
||||
@@ -34,17 +34,19 @@ $router->addRoute('GET', '/lang/{locale}', function ($locale) {
|
||||
$router->addRoute('GET', '/', [ItemController::class, 'overview']);
|
||||
$router->addRoute('GET', '/categories', [CategoryController::class, 'index']);
|
||||
$router->addRoute('GET', '/parts', [ItemController::class, 'addForm']);
|
||||
$router->addRoute('GET', '/print/{id:\d+}', [ItemController::class, 'printQR']);
|
||||
|
||||
// --- API Routes (AJAX Content) ---
|
||||
// These routes return only the Twig block content, not the full layout.
|
||||
$router->addRoute('GET', '/api/items', [ItemController::class, 'listItems']);
|
||||
$router->addRoute('GET', '/api/items/{id:\d+}', [ItemController::class, 'getItem']);
|
||||
$router->addRoute('GET', '/api/categories', [CategoryController::class, 'listCategories']);
|
||||
$router->addRoute('GET', '/api/categories/{id:\d+}', [CategoryController::class, 'getCategory']);
|
||||
$router->addRoute('GET', '/api/categories/list', [CategoryController::class, 'listCategoriesJson']);
|
||||
$router->addRoute('GET', '/api/categories/{id}', [CategoryController::class, 'getCategory']);
|
||||
$router->addRoute('GET', '/api/parts', [ItemController::class, 'renderAddForm']);
|
||||
|
||||
// --- API CRUD Routes ---
|
||||
// Items
|
||||
$router->addRoute('GET', '/api/items/{id:\d+}', [ItemController::class, 'getItem']);
|
||||
$router->addRoute('POST', '/api/items', [ItemController::class, 'create']);
|
||||
$router->addRoute('PUT', '/api/items/{id:\d+}', [ItemController::class, 'update']);
|
||||
$router->addRoute('DELETE', '/api/items/{id:\d+}', [ItemController::class, 'delete']);
|
||||
|
||||
@@ -159,7 +159,7 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
if (data.success) {
|
||||
fetchContent('/parts', false);
|
||||
fetchContent('/', false); // Go to overview to see the new part
|
||||
} else {
|
||||
alert(data.error || 'Error adding part');
|
||||
}
|
||||
@@ -226,9 +226,23 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||
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_item_category_id').value = data.category_id || '';
|
||||
const modal = new bootstrap.Modal(document.getElementById('editItemModal'));
|
||||
modal.show();
|
||||
document.getElementById('edit_location').value = data.location || '';
|
||||
// Populate category select
|
||||
const categorySelect = document.getElementById('edit_item_category_id');
|
||||
categorySelect.innerHTML = '<option value="">-- Select Category --</option>';
|
||||
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');
|
||||
}
|
||||
@@ -241,7 +255,7 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||
|
||||
function handleDeleteItem() {
|
||||
const id = this.getAttribute('data-id');
|
||||
if (confirm('Are you sure you want to delete this part?')) {
|
||||
if (confirm(window.translations.deletePartConfirm)) {
|
||||
fetch('/api/items/' + id, {
|
||||
method: 'DELETE'
|
||||
})
|
||||
@@ -285,30 +299,32 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||
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 data = {
|
||||
item_name: name,
|
||||
item_description: description,
|
||||
category_id: categoryId || null
|
||||
};
|
||||
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',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify(data)
|
||||
body: formData
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
if (data.success) {
|
||||
bootstrap.Modal.getInstance(document.getElementById('editItemModal')).hide();
|
||||
fetchContent('/overview', false); // Assuming current is overview
|
||||
fetchContent('/', false); // Reload overview
|
||||
} else {
|
||||
alert(data.error || 'Error updating part');
|
||||
}
|
||||
@@ -360,27 +376,47 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||
const id = this.getAttribute('data-id');
|
||||
// Fetch current category data
|
||||
fetch('/api/categories/' + id)
|
||||
.then(response => response.json())
|
||||
.then(response => {
|
||||
if (!response.ok) {
|
||||
throw new Error('HTTP ' + response.status + ': ' + response.statusText);
|
||||
}
|
||||
return response.json();
|
||||
})
|
||||
.then(data => {
|
||||
if (data) {
|
||||
if (data && !data.error) {
|
||||
document.getElementById('edit_category_id').value = data.id;
|
||||
document.getElementById('edit_category_name').value = data.name;
|
||||
document.getElementById('edit_parent_category_id').value = data.parent_id || '';
|
||||
const modal = new bootstrap.Modal(document.getElementById('editCategoryModal'));
|
||||
modal.show();
|
||||
// Populate parent select, excluding current category
|
||||
const parentSelect = document.getElementById('edit_parent_category_id');
|
||||
parentSelect.innerHTML = '<option value="">-- No Parent --</option>';
|
||||
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('Category not found');
|
||||
alert(data.error || 'Category not found');
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Error:', error);
|
||||
alert('Error fetching category');
|
||||
alert('Error fetching category: ' + error.message);
|
||||
});
|
||||
}
|
||||
|
||||
function handleDeleteCategory() {
|
||||
const id = this.getAttribute('data-id');
|
||||
if (confirm('Are you sure you want to delete this category?')) {
|
||||
if (confirm(window.translations.deleteCategoryConfirm)) {
|
||||
fetch('/api/categories/' + id, {
|
||||
method: 'DELETE'
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user