Fix workflow: restore split increments, fix print view, add move modal, adjust Phase 1 card heights
All checks were successful
Docker Build & Push / build-and-push (push) Successful in 14s
All checks were successful
Docker Build & Push / build-and-push (push) Successful in 14s
- Reverted adjust_single to use UPDATE when an item is already on the table, fixing the first-click bug and preventing duplicate rows in Phase 1. - Added a modal 'Move' button to items in Phase 2 allowing them to be moved between backpacks and compartments directly. - Further reduced the height of Lager cards to remove empty space below buttons. - Fixed the print view by updating the SQL query and adding manufacturer + product designation to the rows.
This commit is contained in:
@@ -159,17 +159,17 @@ $conn->close();
|
||||
border: 1px solid #eee; border-radius: 8px; padding: 6px;
|
||||
text-align: center; background: #fff; display: flex; flex-direction: column;
|
||||
transition: transform 0.1s, box-shadow 0.1s;
|
||||
height: 200px;
|
||||
height: 165px;
|
||||
}
|
||||
.lager-card:hover { transform: translateY(-2px); box-shadow: 0 4px 8px rgba(0,0,0,0.05); }
|
||||
.lager-img-wrapper {
|
||||
width: 100%; height: 90px; margin-bottom: 6px; display: flex;
|
||||
width: 100%; height: 75px; margin-bottom: 4px; display: flex;
|
||||
align-items: center; justify-content: center; overflow: hidden;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.lager-img-wrapper img.lager-card-img {
|
||||
width: 100% !important; height: 100% !important;
|
||||
max-width: 100% !important; max-height: 90px !important;
|
||||
max-width: 100% !important; max-height: 75px !important;
|
||||
object-fit: contain !important; border-radius: 4px;
|
||||
}
|
||||
.lager-title {
|
||||
@@ -178,12 +178,12 @@ $conn->close();
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.lager-meta {
|
||||
font-size: 0.7em; color: #6c757d; margin-bottom: 4px;
|
||||
font-size: 0.7em; color: #6c757d; margin-bottom: 2px;
|
||||
white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
|
||||
line-height: 1.1; flex-shrink: 0;
|
||||
}
|
||||
.lager-controls {
|
||||
display: flex; justify-content: center; align-items: center; gap: 8px; margin-top: auto;
|
||||
display: flex; justify-content: center; align-items: center; gap: 8px; margin-top: auto; margin-bottom: 2px;
|
||||
}
|
||||
.table-status-text {
|
||||
font-size: 0.75em; color: #2e7d32; font-weight: 600;
|
||||
@@ -345,6 +345,27 @@ $conn->close();
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="modal fade" id="moveItemModal" tabindex="-1" aria-labelledby="moveItemModalLabel" aria-hidden="true">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title" id="moveItemModalLabel">Artikel verschieben</h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<p>Wohin möchtest du diesen Artikel verschieben?</p>
|
||||
<select id="move-destination-select" class="form-select">
|
||||
<!-- Populated via JS -->
|
||||
</select>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Abbrechen</button>
|
||||
<button type="button" class="btn btn-primary" id="btn-move-save">Verschieben</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="save-feedback" class="save-feedback">Änderungen gespeichert!</div>
|
||||
<div id="image-preview-tooltip" class="image-preview-tooltip"></div>
|
||||
|
||||
@@ -566,14 +587,6 @@ $conn->close();
|
||||
|
||||
function adjustTable(articleId, delta, includeChildren = false) {
|
||||
sendApiRequest({ action: 'adjust_table_quantity', article_id: articleId, delta: delta, include_children: includeChildren })
|
||||
.then(() => {
|
||||
return fetch('api_packing_list_handler.php', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ action: 'get_items', packing_list_id: packingListId }) // Fallback to get items
|
||||
});
|
||||
})
|
||||
.then(res => res.json())
|
||||
.then(newItems => {
|
||||
if(Array.isArray(newItems)) packedItems = newItems;
|
||||
// Restore scroll positions
|
||||
@@ -733,7 +746,8 @@ $conn->close();
|
||||
let metaDisplay = metaDisplayArr.length > 0 ? `<small class="text-muted">(${metaDisplayArr.join(' - ')})</small>` : '';
|
||||
|
||||
let controls = `
|
||||
<input type="number" class="form-control form-control-sm quantity-input text-center mx-2" value="${item.quantity}" min="1" style="width:60px;">
|
||||
<button class="btn btn-sm btn-outline-secondary move-item-btn" title="Verschieben"><i class="fas fa-exchange-alt"></i></button>
|
||||
<input type="number" class="form-control form-control-sm quantity-input text-center mx-1" value="${item.quantity}" min="1" style="width:50px;">
|
||||
<button class="btn btn-sm btn-outline-danger remove-item-btn"><i class="fas fa-trash"></i></button>
|
||||
`;
|
||||
|
||||
@@ -811,6 +825,8 @@ $conn->close();
|
||||
}, 300);
|
||||
}
|
||||
|
||||
let itemToMoveId = null;
|
||||
let itemToMoveEl = null;
|
||||
let itemToRemoveId = null;
|
||||
let itemToRemoveEl = null;
|
||||
|
||||
@@ -821,6 +837,59 @@ $conn->close();
|
||||
if (!itemEl) return;
|
||||
const itemId = itemEl.dataset.itemId;
|
||||
|
||||
if (button.classList.contains('move-item-btn')) {
|
||||
itemToMoveId = itemId;
|
||||
itemToMoveEl = itemEl;
|
||||
|
||||
const selectEl = document.getElementById('move-destination-select');
|
||||
selectEl.innerHTML = '<option value="table">Zurück auf den Tisch</option>';
|
||||
|
||||
// Populate select with all containers
|
||||
const containers = packedItems.filter(item => item.backpack_id || item.backpack_compartment_id);
|
||||
// First add backpacks
|
||||
containers.filter(c => c.backpack_id).forEach(bp => {
|
||||
const opt = document.createElement('option');
|
||||
opt.value = bp.id;
|
||||
opt.textContent = `👜 Rucksack: ${bp.name}`;
|
||||
// Get the carrier Name
|
||||
const carrier = carriers.find(c => String(c.id) === String(bp.carrier_user_id));
|
||||
if (carrier) opt.textContent += ` (${carrier.username})`;
|
||||
selectEl.appendChild(opt);
|
||||
|
||||
// Add its compartments
|
||||
containers.filter(c => c.parent_packing_list_item_id === bp.id).forEach(comp => {
|
||||
const optComp = document.createElement('option');
|
||||
optComp.value = comp.id;
|
||||
optComp.textContent = ` ↳ 📁 Fach: ${comp.name}`;
|
||||
selectEl.appendChild(optComp);
|
||||
});
|
||||
});
|
||||
|
||||
if (!window.moveItemModalInstance) {
|
||||
window.moveItemModalInstance = new bootstrap.Modal(document.getElementById('moveItemModal'));
|
||||
document.getElementById('btn-move-save').onclick = () => {
|
||||
const destId = document.getElementById('move-destination-select').value;
|
||||
window.moveItemModalInstance.hide();
|
||||
|
||||
if (destId === 'table') {
|
||||
document.getElementById('table-container').appendChild(itemToMoveEl);
|
||||
} else {
|
||||
// Find the container element
|
||||
const destEl = document.querySelector(`.packed-item-container[data-item-id="${destId}"] > .nested-sortable`);
|
||||
if (destEl) {
|
||||
destEl.appendChild(itemToMoveEl);
|
||||
} else {
|
||||
alert("Fehler: Ziel-Fach nicht in der aktuellen Ansicht gefunden.");
|
||||
return;
|
||||
}
|
||||
}
|
||||
syncListState();
|
||||
};
|
||||
}
|
||||
window.moveItemModalInstance.show();
|
||||
return;
|
||||
}
|
||||
|
||||
if (button.classList.contains('remove-item-btn')) {
|
||||
const isTable = (itemEl.closest('#table-container') !== null);
|
||||
if (isTable) {
|
||||
|
||||
@@ -48,9 +48,11 @@ if ($packing_list) {
|
||||
pli.carrier_user_id, pli.backpack_id, pli.backpack_compartment_id,
|
||||
COALESCE(a.name, pli.name, bp.name, bpc.name, 'Unbekannt') AS name,
|
||||
a.weight_grams, c.name AS category_name, a.consumable, a.image_url,
|
||||
a.product_designation, m.name AS manufacturer_name,
|
||||
u.username AS carrier_name
|
||||
FROM packing_list_items pli
|
||||
LEFT JOIN articles a ON pli.article_id = a.id
|
||||
LEFT JOIN manufacturers m ON a.manufacturer_id = m.id
|
||||
LEFT JOIN categories c ON a.category_id = c.id
|
||||
LEFT JOIN backpacks bp ON pli.backpack_id = bp.id
|
||||
LEFT JOIN backpack_compartments bpc ON pli.backpack_compartment_id = bpc.id
|
||||
@@ -129,7 +131,13 @@ function render_rows_recursive($items, $level = 0) {
|
||||
// Name
|
||||
echo '<td style="padding-left: ' . $indent . 'px;">';
|
||||
if ($level > 0 && !$is_container) echo '<span style="color:#ccc; margin-right:5px;">↳</span>';
|
||||
echo '<span style="' . $name_style . '">' . htmlspecialchars($item['name']) . '</span>';
|
||||
|
||||
$meta = [];
|
||||
if (!empty($item['manufacturer_name'])) $meta[] = $item['manufacturer_name'];
|
||||
if (!empty($item['product_designation'])) $meta[] = $item['product_designation'];
|
||||
$meta_str = !empty($meta) && !$is_container ? ' <small style="color:#666;">(' . htmlspecialchars(implode(' - ', $meta)) . ')</small>' : '';
|
||||
|
||||
echo '<span style="' . $name_style . '">' . htmlspecialchars($item['name']) . '</span>' . $meta_str;
|
||||
echo '</td>';
|
||||
|
||||
// Qty
|
||||
|
||||
Reference in New Issue
Block a user