Fix: Rucksack-Kategorie (DB/UI/Stats) und Tooltip-Zuverlässigkeit
All checks were successful
Docker Build & Push / build-and-push (push) Successful in 28s

This commit is contained in:
Gemini Agent
2025-12-07 08:04:32 +00:00
parent 80ede8e3a6
commit 991f264e29
2 changed files with 68 additions and 21 deletions

View File

@@ -30,6 +30,11 @@ $check_col = $conn->query("SHOW COLUMNS FROM backpacks LIKE 'product_url'");
if ($check_col && $check_col->num_rows == 0) {
$conn->query("ALTER TABLE backpacks ADD COLUMN product_url VARCHAR(255) DEFAULT NULL AFTER image_url");
}
$check_col_cat = $conn->query("SHOW COLUMNS FROM backpacks LIKE 'category_id'");
if ($check_col_cat && $check_col_cat->num_rows == 0) {
$conn->query("ALTER TABLE backpacks ADD COLUMN category_id INT DEFAULT NULL");
$conn->query("ALTER TABLE backpacks ADD CONSTRAINT fk_bp_category FOREIGN KEY (category_id) REFERENCES categories(id) ON DELETE SET NULL");
}
$check_col_c = $conn->query("SHOW COLUMNS FROM backpack_compartments LIKE 'linked_article_id'");
if ($check_col_c && $check_col_c->num_rows == 0) {
$conn->query("ALTER TABLE backpack_compartments ADD COLUMN linked_article_id INT DEFAULT NULL");
@@ -103,14 +108,7 @@ if ($backpack_id > 0) {
}
}
// Load Manufacturers
$stmt_man_load = $conn->prepare("SELECT id, name FROM manufacturers WHERE user_id = ? ORDER BY name ASC");
$stmt_man_load->bind_param("i", $user_id);
$stmt_man_load->execute();
$manufacturers = $stmt_man_load->get_result()->fetch_all(MYSQLI_ASSOC);
$stmt_man_load->close();
// Load Articles for Linked Compartments
// Load Manufacturers & Categories
$hh_ids = [$user_id];
if ($household_id) {
$stmt_hhm = $conn->prepare("SELECT id FROM users WHERE household_id = ?");
@@ -121,6 +119,22 @@ if ($household_id) {
}
$placeholders = implode(',', array_fill(0, count($hh_ids), '?'));
$types_hh = str_repeat('i', count($hh_ids));
// Manufacturers
$stmt_man_load = $conn->prepare("SELECT id, name FROM manufacturers WHERE user_id IN ($placeholders) ORDER BY name ASC");
$stmt_man_load->bind_param($types_hh, ...$hh_ids);
$stmt_man_load->execute();
$manufacturers = $stmt_man_load->get_result()->fetch_all(MYSQLI_ASSOC);
$stmt_man_load->close();
// Categories
$stmt_cat_load = $conn->prepare("SELECT id, name FROM categories WHERE user_id IN ($placeholders) ORDER BY name ASC");
$stmt_cat_load->bind_param($types_hh, ...$hh_ids);
$stmt_cat_load->execute();
$categories = $stmt_cat_load->get_result()->fetch_all(MYSQLI_ASSOC);
$stmt_cat_load->close();
// Load Articles for Linked Compartments
$stmt_arts = $conn->prepare("SELECT id, name, weight_grams, image_url FROM articles WHERE user_id IN ($placeholders) OR household_id = ? ORDER BY name ASC");
$params_arts = array_merge($hh_ids, [$household_id ?: 0]);
$types_arts = $types_hh . 'i';
@@ -134,6 +148,7 @@ $stmt_arts->close();
if ($_SERVER['REQUEST_METHOD'] == 'POST') {
$name = trim($_POST['name']);
// Manufacturer Logic
$manufacturer = '';
if (isset($_POST['manufacturer_select'])) {
if ($_POST['manufacturer_select'] === 'new') {
@@ -169,6 +184,7 @@ if ($_SERVER['REQUEST_METHOD'] == 'POST') {
$volume = intval($_POST['volume_liters']);
$share_household = isset($_POST['share_household']) ? 1 : 0;
$product_url_input = trim($_POST['product_url'] ?? '');
$category_id = !empty($_POST['category_id']) ? intval($_POST['category_id']) : NULL;
$image_url_for_db = $image_url;
$pasted_image = $_POST['pasted_image_data'] ?? '';
@@ -194,12 +210,12 @@ if ($_SERVER['REQUEST_METHOD'] == 'POST') {
$final_household_id = ($share_household && $household_id) ? $household_id : NULL;
if ($backpack_id > 0) {
$stmt = $conn->prepare("UPDATE backpacks SET name=?, manufacturer=?, model=?, weight_grams=?, volume_liters=?, household_id=?, image_url=?, product_url=? WHERE id=? AND user_id=?");
$stmt->bind_param("sssiisssii", $name, $manufacturer, $model, $weight, $volume, $final_household_id, $image_url_for_db, $product_url_input, $backpack_id, $user_id);
$stmt = $conn->prepare("UPDATE backpacks SET name=?, manufacturer=?, model=?, weight_grams=?, volume_liters=?, household_id=?, image_url=?, product_url=?, category_id=? WHERE id=? AND user_id=?");
$stmt->bind_param("sssiisssiii", $name, $manufacturer, $model, $weight, $volume, $final_household_id, $image_url_for_db, $product_url_input, $category_id, $backpack_id, $user_id);
$stmt->execute();
} else {
$stmt = $conn->prepare("INSERT INTO backpacks (user_id, household_id, name, manufacturer, model, weight_grams, volume_liters, image_url, product_url) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)");
$stmt->bind_param("iisssiiss", $user_id, $final_household_id, $name, $manufacturer, $model, $weight, $volume, $image_url_for_db, $product_url_input);
$stmt = $conn->prepare("INSERT INTO backpacks (user_id, household_id, name, manufacturer, model, weight_grams, volume_liters, image_url, product_url, category_id) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)");
$stmt->bind_param("iisssiissi", $user_id, $final_household_id, $name, $manufacturer, $model, $weight, $volume, $image_url_for_db, $product_url_input, $category_id);
$stmt->execute();
$backpack_id = $stmt->insert_id;
}
@@ -309,6 +325,19 @@ require_once 'header.php';
<input type="text" name="new_manufacturer_name" class="form-control form-control-sm" placeholder="Name des neuen Herstellers">
</div>
</div>
<div class="col-md-6">
<label class="form-label">Kategorie</label>
<select class="form-select" id="category_id" name="category_id">
<option value="">-- Keine (Sonstiges) --</option>
<?php
$current_cat = $backpack['category_id'] ?? 0;
foreach ($categories as $cat):
$selected = ($cat['id'] == $current_cat) ? 'selected' : '';
?>
<option value="<?php echo $cat['id']; ?>" <?php echo $selected; ?>><?php echo htmlspecialchars($cat['name']); ?></option>
<?php endforeach; ?>
</select>
</div>
<div class="col-md-6">
<label class="form-label">Modell</label>
<input type="text" name="model" class="form-control" value="<?php echo htmlspecialchars($backpack['model'] ?? ''); ?>">
@@ -368,7 +397,7 @@ require_once 'header.php';
</select>
<!-- Text Input -->
<input type="text" name="comp_names[]" class="form-control name-input" placeholder="Fachname">
<input type="text" name="comp_names[]" class="form-control name-input" value="Hauptfach" placeholder="Fachname">
<!-- Article Select -->
<div class="flex-grow-1 article-select-wrapper" style="display:none;">
@@ -456,6 +485,11 @@ document.addEventListener('DOMContentLoaded', function() {
manSelect.addEventListener('change', function() { toggleManContainer(this.value); });
new TomSelect(manSelect, { create: false, sortField: { field: "text", direction: "asc" }, onChange: function(value) { toggleManContainer(value); } });
}
// New: Category Select
if (document.getElementById('category_id')) {
new TomSelect('#category_id', { create: false, sortField: { field: "text", direction: "asc" } });
}
const container = document.getElementById('compartments-container');
@@ -601,4 +635,4 @@ document.addEventListener('DOMContentLoaded', function() {
});
</script>
<?php require_once 'footer.php'; ?>
<?php require_once 'footer.php'; ?>

View File

@@ -60,13 +60,14 @@ $stmt_list_owner->close();
$page_title = "Packliste: " . htmlspecialchars($packing_list['name']);
// FIX: Join Categories also for Backpacks
$sql = "SELECT
pli.id, pli.quantity, pli.parent_packing_list_item_id, pli.carrier_user_id,
pli.backpack_id, pli.backpack_compartment_id,
COALESCE(a.name, pli.name, bp.name, bpc.name, 'Unbekanntes Item') AS article_name,
COALESCE(a.weight_grams, bp.weight_grams, 0) as weight_grams,
a.image_url, a.product_designation, a.consumable,
c.name AS category_name,
COALESCE(c.name, c_bp.name, 'Sonstiges') AS category_name,
m.name AS manufacturer_name,
u.username AS carrier_name,
u.id AS carrier_id
@@ -75,6 +76,7 @@ $sql = "SELECT
LEFT JOIN backpacks AS bp ON pli.backpack_id = bp.id
LEFT JOIN backpack_compartments AS bpc ON pli.backpack_compartment_id = bpc.id
LEFT JOIN categories AS c ON a.category_id = c.id
LEFT JOIN categories AS c_bp ON bp.category_id = c_bp.id
LEFT JOIN manufacturers AS m ON a.manufacturer_id = m.id
LEFT JOIN users AS u ON pli.carrier_user_id = u.id
WHERE pli.packing_list_id = ?
@@ -394,19 +396,30 @@ function render_item_row($item, $level, $items_by_parent) {
const carrierStatsData = <?php echo json_encode($carrier_stats_details); ?>;
document.addEventListener('DOMContentLoaded', function() {
// Tooltip logic
// Tooltip logic with EVENT DELEGATION
const tooltip = document.getElementById('image-preview-tooltip');
if (tooltip) {
document.querySelectorAll('.article-image-trigger').forEach(trigger => {
trigger.addEventListener('mouseover', e => {
document.body.addEventListener('mouseover', function(e) {
if (e.target.classList.contains('article-image-trigger')) {
const url = e.target.getAttribute('data-preview-url');
if (url && !url.endsWith('assets/images/keinbild.png')) {
tooltip.style.backgroundImage = `url('${url}')`;
tooltip.style.display = 'block';
}
});
trigger.addEventListener('mousemove', e => { tooltip.style.left = e.pageX + 15 + 'px'; tooltip.style.top = e.pageY + 15 + 'px'; });
trigger.addEventListener('mouseout', () => { tooltip.style.display = 'none'; });
}
});
document.body.addEventListener('mousemove', function(e) {
if (tooltip.style.display === 'block') {
tooltip.style.left = e.pageX + 15 + 'px';
tooltip.style.top = e.pageY + 15 + 'px';
}
});
document.body.addEventListener('mouseout', function(e) {
if (e.target.classList.contains('article-image-trigger')) {
tooltip.style.display = 'none';
}
});
}
const tooltipTriggerList = [].slice.call(document.querySelectorAll('[data-bs-toggle="tooltip"]'));