UI Fixes: Volle Bildschirmhöhe für Editor, ToDo-Redesign (ohne schwarzen Rahmen) und Bugfixes in Packlisten-Details
All checks were successful
Docker Build & Push / build-and-push (push) Successful in 36s

This commit is contained in:
Gemini
2026-05-13 20:19:29 +00:00
parent c001ea6f62
commit b336f6cabf
3 changed files with 381 additions and 11 deletions

View File

@@ -0,0 +1,367 @@
<?php
// edit_packing_list_details.php - Bearbeiten von Details und Rucksack-Zuweisung
$page_title = "Packliste Details bearbeiten";
if (session_status() == PHP_SESSION_NONE) {
session_start();
}
if (!isset($_SESSION['user_id'])) {
header("Location: login.php");
exit;
}
require_once 'db_connect.php';
require_once 'household_actions.php';
require_once 'backpack_utils.php';
require_once 'header.php';
$current_user_id = $_SESSION['user_id'];
$packing_list_id = isset($_GET['id']) ? intval($_GET['id']) : 0;
$message = '';
$packing_list = null;
$can_edit = false;
// --- 1. Permissions & Basic Data ---
if ($packing_list_id > 0) {
$stmt_household_check = $conn->prepare("SELECT household_id FROM users WHERE id = ?");
$stmt_household_check->bind_param("i", $current_user_id);
$stmt_household_check->execute();
$current_user_household_id = $stmt_household_check->get_result()->fetch_assoc()['household_id'];
$stmt_household_check->close();
$stmt_list_check = $conn->prepare("SELECT id, name, description, user_id, household_id, is_template, todo_list_id FROM packing_lists WHERE id = ?");
$stmt_list_check->bind_param("i", $packing_list_id);
$stmt_list_check->execute();
$result = $stmt_list_check->get_result();
if ($result->num_rows == 1) {
$packing_list = $result->fetch_assoc();
$is_owner = ($packing_list['user_id'] == $current_user_id);
$is_household_list = !empty($packing_list['household_id']);
$is_in_same_household = ($is_household_list && $packing_list['household_id'] == $current_user_household_id);
if ($is_owner || $is_in_same_household) {
$can_edit = true;
} else {
$message = '<div class="alert alert-danger">Keine Berechtigung.</div>';
$packing_list = null;
}
} else {
$message = '<div class="alert alert-warning">Packliste nicht gefunden.</div>';
}
} else {
$message = '<div class="alert alert-danger">Keine ID.</div>';
}
// --- 2. Fetch Data for Dropdowns ---
$available_users = [];
$available_todo_lists = [];
if ($can_edit) {
// Owners: Creator + Household Members (if shared)
if ($packing_list['household_id']) {
$stmt = $conn->prepare("SELECT id, COALESCE(NULLIF(display_name, ''), username) AS username FROM users WHERE household_id = ?");
$stmt->bind_param("i", $packing_list['household_id']);
} else {
$stmt = $conn->prepare("SELECT id, COALESCE(NULLIF(display_name, ''), username) AS username FROM users WHERE id = ?");
$stmt->bind_param("i", $packing_list['user_id']);
}
$stmt->execute();
$res = $stmt->get_result();
while ($row = $res->fetch_assoc()) {
$available_users[] = $row;
}
// Fetch Todo Lists
$stmt_tl = $conn->prepare("SELECT id, name FROM todo_lists WHERE user_id = ? OR (household_id IS NOT NULL AND household_id = ?)");
$stmt_tl->bind_param("ii", $current_user_id, $current_user_household_id);
$stmt_tl->execute();
$res_tl = $stmt_tl->get_result();
while ($row = $res_tl->fetch_assoc()) {
$available_todo_lists[] = $row;
}
$stmt_tl->close();
// Current Assignments
$current_assignments = [];
$stmt_ca = $conn->prepare("SELECT user_id, backpack_id FROM packing_list_carriers WHERE packing_list_id = ?");
$stmt_ca->bind_param("i", $packing_list_id);
$stmt_ca->execute();
$res_ca = $stmt_ca->get_result();
while ($row = $res_ca->fetch_assoc()) {
$current_assignments[$row['user_id']] = $row['backpack_id'];
}
}
// --- 3. Handle Form Submission ---
if ($_SERVER["REQUEST_METHOD"] == "POST" && $can_edit) {
// Update Basic Details
$name = trim($_POST['name']);
$description = trim($_POST['description']);
// Household sharing logic
$new_household_id = NULL;
if (isset($_POST['is_household_list']) && $_POST['is_household_list'] == '1' && $current_user_household_id) {
$new_household_id = $current_user_household_id;
}
$todo_list_id = !empty($_POST['todo_list_id']) ? intval($_POST['todo_list_id']) : NULL;
$stmt_update = $conn->prepare("UPDATE packing_lists SET name = ?, description = ?, household_id = ?, todo_list_id = ? WHERE id = ?");
$stmt_update->bind_param("sssii", $name, $description, $new_household_id, $todo_list_id, $packing_list_id);
$stmt_update->execute();
$packing_list['name'] = $name;
$packing_list['description'] = $description;
$packing_list['household_id'] = $new_household_id;
$packing_list['todo_list_id'] = $todo_list_id;
// Handle Participation & Backpacks
// Get all potential users to check if they were unchecked
$participate_map = $_POST['participate'] ?? []; // Array of UserID => "1" if checked
foreach ($available_users as $user) {
$uid = $user['id'];
$is_checked = isset($participate_map[$uid]);
$was_participating = array_key_exists($uid, $current_assignments);
if ($is_checked) {
// User participates -> Update/Insert Backpack
$bid = isset($_POST['backpacks'][$uid]) ? intval($_POST['backpacks'][$uid]) : 0;
$bid = ($bid > 0) ? $bid : NULL;
if ($was_participating) {
$old_bid = $current_assignments[$uid];
// Update if changed
if ($old_bid != $bid) {
$stmt_up = $conn->prepare("UPDATE packing_list_carriers SET backpack_id = ? WHERE packing_list_id = ? AND user_id = ?");
$stmt_up->bind_param("iii", $bid, $packing_list_id, $uid);
$stmt_up->execute();
// Cleanup Old Containers
cleanup_old_backpack_containers($conn, $packing_list_id, $uid);
// Sync New
if ($bid) {
sync_backpack_items($conn, $packing_list_id, $uid, $bid);
}
}
} else {
// New Participant
$stmt_in = $conn->prepare("INSERT INTO packing_list_carriers (packing_list_id, user_id, backpack_id) VALUES (?, ?, ?)");
$stmt_in->bind_param("iii", $packing_list_id, $uid, $bid);
$stmt_in->execute();
if ($bid) {
sync_backpack_items($conn, $packing_list_id, $uid, $bid);
}
}
} else {
// User NOT checked -> Remove if existed
if ($was_participating) {
$conn->query("DELETE FROM packing_list_carriers WHERE packing_list_id = $packing_list_id AND user_id = $uid");
// Cleanup Items: Delete all items carried by this user?
// Or move to unassigned? Let's move to unassigned (NULL) to be safe against data loss.
// But Containers (Backpacks) should be deleted.
// 1. Delete Containers
cleanup_old_backpack_containers($conn, $packing_list_id, $uid);
// 2. Move remaining items (loose items) to unassigned?
$conn->query("UPDATE packing_list_items SET carrier_user_id = NULL WHERE packing_list_id = $packing_list_id AND carrier_user_id = $uid");
}
}
}
$message = '<div class="alert alert-success">Änderungen gespeichert!</div>';
// Refresh assignments
$stmt_ca->execute();
$res_ca = $stmt_ca->get_result();
$current_assignments = [];
while ($row = $res_ca->fetch_assoc()) {
$current_assignments[$row['user_id']] = $row['backpack_id'];
}
}
function cleanup_old_backpack_containers($conn, $list_id, $user_id) {
// Find all container items for this user (backpacks or compartments)
$stmt = $conn->prepare("SELECT id FROM packing_list_items WHERE packing_list_id = ? AND carrier_user_id = ? AND (backpack_id IS NOT NULL OR backpack_compartment_id IS NOT NULL)");
$stmt->bind_param("ii", $list_id, $user_id);
$stmt->execute();
$res = $stmt->get_result();
$container_ids = [];
while ($r = $res->fetch_assoc()) $container_ids[] = $r['id'];
if (!empty($container_ids)) {
$ids_str = implode(',', $container_ids);
// Unparent children so they don't get deleted (or keep them and they get deleted? No, save content)
// Set parent to NULL for children of these containers
$conn->query("UPDATE packing_list_items SET parent_packing_list_item_id = NULL WHERE packing_list_id = $list_id AND parent_packing_list_item_id IN ($ids_str)");
// Delete containers
$conn->query("DELETE FROM packing_list_items WHERE id IN ($ids_str)");
}
}
$back_link = 'packing_lists.php' . (!empty($packing_list['is_template']) ? '?view=templates' : '');
$page_headline = !empty($packing_list['is_template']) ? 'Vorlage bearbeiten' : 'Details bearbeiten';
?>
<div class="card">
<div class="card-header d-flex justify-content-between align-items-center">
<h2 class="h4 mb-0"><?php echo $page_headline; ?>: <?php echo htmlspecialchars($packing_list['name'] ?? ''); ?></h2>
<a href="<?php echo $back_link; ?>" class="btn btn-sm btn-outline-light"><i class="fas fa-arrow-left me-2"></i>Zurück</a>
</div>
<div class="card-body p-4">
<?php echo $message; ?>
<?php if ($packing_list): ?>
<form method="post" id="editListForm">
<div class="row">
<div class="col-md-8">
<h5 class="mb-3">Basisdaten</h5>
<div class="mb-3">
<label class="form-label">Name</label>
<input type="text" class="form-control" name="name" value="<?php echo htmlspecialchars($packing_list['name']); ?>" required>
</div>
<div class="mb-3">
<label class="form-label">Beschreibung</label>
<textarea class="form-control" name="description" rows="3"><?php echo htmlspecialchars($packing_list['description'] ?: ''); ?></textarea>
</div>
<?php if (empty($packing_list['is_template'])): ?>
<div class="mb-4">
<label for="todo_list_id" class="form-label fw-bold">ToDo-Liste (optional)</label>
<select class="form-select" id="todo_list_id" name="todo_list_id">
<option value="">-- Keine ToDo-Liste --</option>
<?php foreach ($available_todo_lists as $tl): ?>
<option value="<?php echo $tl['id']; ?>" <?php if (($packing_list['todo_list_id'] ?? null) == $tl['id']) echo 'selected'; ?>>
<?php echo htmlspecialchars($tl['name']); ?>
</option>
<?php endforeach; ?>
</select>
<div class="form-text">Verknüpfe eine ToDo-Liste mit dieser Packliste.</div>
</div>
<?php endif; ?>
<?php if ($current_user_household_id): ?>
<div class="form-check form-switch mb-3">
<input class="form-check-input" type="checkbox" role="switch" id="is_household_list" name="is_household_list" value="1" <?php echo !empty($packing_list['household_id']) ? 'checked' : ''; ?>>
<label class="form-check-label" for="is_household_list">Für den gesamten Haushalt freigeben</label>
</div>
<?php endif; ?>
</div>
<div class="col-md-4">
<h5 class="mb-3">Teilnehmer & Rucksäcke <?php if(!empty($packing_list['is_template'])): ?><small class="text-muted">(Standard)</small><?php endif; ?></h5>
<div class="card bg-light border-0">
<div class="card-body">
<p class="small text-muted">Wähle hier, wer mitkommt und welchen Rucksack er trägt.</p>
<div id="backpack-warning" class="alert alert-warning d-none small p-2 mb-2">
<i class="fas fa-exclamation-triangle me-1"></i> Ein Rucksack wurde mehrfach ausgewählt!
</div>
<?php
$all_assigned_backpack_ids = array_values($current_assignments);
$user_backpacks_json = [];
foreach ($available_users as $user):
$user_backpacks = get_available_backpacks_for_user($conn, $user['id'], $packing_list['household_id']);
$is_participating = array_key_exists($user['id'], $current_assignments);
$my_current_bp_id = $current_assignments[$user['id']] ?? 0;
$user_backpacks_json[$user['id']] = [
'current_id' => $my_current_bp_id,
'backpacks' => $user_backpacks
];
?>
<div class="mb-4 border-bottom pb-3">
<div class="form-check mb-2">
<input class="form-check-input participation-check" type="checkbox" name="participate[<?php echo $user['id']; ?>]" value="1" <?php echo $is_participating ? 'checked' : ''; ?> id="part_<?php echo $user['id']; ?>">
<label class="form-check-label fw-bold" for="part_<?php echo $user['id']; ?>">
<?php echo htmlspecialchars($user['username']); ?>
</label>
</div>
<div class="ms-4 backpack-selector-wrapper" style="<?php echo $is_participating ? '' : 'opacity: 0.5; pointer-events: none;'; ?>">
<?php echo render_backpack_card_selector($user, $my_current_bp_id, $user_backpacks); ?>
</div>
</div>
<?php endforeach; ?>
</div>
</div>
</div>
</div>
<hr class="my-4">
<div class="d-flex justify-content-between">
<a href="manage_packing_list_items.php?id=<?php echo $packing_list_id; ?>" class="btn btn-info text-white"><i class="fas fa-boxes me-2"></i>Inhalt bearbeiten</a>
<button type="submit" class="btn btn-primary" id="submitBtn"><i class="fas fa-save me-2"></i>Speichern & Synchronisieren</button>
</div>
</form>
<?php endif; ?>
</div>
</div>
<!-- Render Modal JS -->
<?php echo render_backpack_modal_script($user_backpacks_json, $all_assigned_backpack_ids); ?>
<script>
document.addEventListener('DOMContentLoaded', function() {
const submitBtn = document.getElementById('submitBtn');
const warning = document.getElementById('backpack-warning');
// Toggle Opacity logic
document.querySelectorAll('.participation-check').forEach(chk => {
chk.addEventListener('change', function() {
const wrapper = this.closest('.mb-4').querySelector('.backpack-selector-wrapper');
if (this.checked) {
wrapper.style.opacity = '1';
wrapper.style.pointerEvents = 'auto';
} else {
wrapper.style.opacity = '0.5';
wrapper.style.pointerEvents = 'none';
}
validateBackpacks();
});
});
// Validation Logic (Copied from add_packing_list.php)
function validateBackpacks() {
const selected = [];
let hasDuplicate = false;
const inputs = document.querySelectorAll('input[name^="backpacks["]');
inputs.forEach(input => {
const userIdMatch = input.name.match(/backpacks\[(\d+)\]/);
if (userIdMatch) {
const userId = userIdMatch[1];
const partCheck = document.getElementById('part_' + userId);
if (partCheck && partCheck.checked) {
const val = parseInt(input.value);
if (val > 0) {
if (selected.includes(val)) {
hasDuplicate = true;
}
selected.push(val);
}
}
}
});
if (hasDuplicate) {
warning.classList.remove('d-none');
submitBtn.disabled = true;
} else {
warning.classList.add('d-none');
submitBtn.disabled = false;
}
}
document.body.addEventListener('hidden.bs.modal', validateBackpacks);
validateBackpacks();
});
</script>
<?php require_once 'footer.php'; ?>

View File

@@ -242,11 +242,11 @@ $conn->close();
</div>
</div>
<div class="row g-2 flex-nowrap" style="height: calc(100vh - 220px); min-height: 600px; overflow-x: auto;" id="main-panes-container">
<div class="row g-2 flex-nowrap" style="height: calc(100vh - 160px); min-height: 600px; overflow-x: auto;" id="main-panes-container">
<!-- LAGER -->
<div class="<?php echo $col_class_lager; ?> h-100" id="pane-lager">
<div class="card h-100">
<div class="card-header bg-light">
<div class="card h-100 d-flex flex-column border-0 shadow-sm">
<div class="card-header bg-light flex-shrink-0">
<h5 class="mb-2"><i class="fas fa-boxes me-2"></i>Lagerbestand</h5>
<div class="row g-2">
<div class="col-12"><input type="text" id="filter-text" class="form-control form-control-sm" placeholder="Artikel suchen..."></div>
@@ -256,7 +256,7 @@ $conn->close();
<div class="col-sm-6 col-md-3"><select id="sort-by" class="form-select form-select-sm"><option value="name_asc">Alphabet (A-Z)</option><option value="weight_asc" selected>Gewicht (Leicht->Schwer)</option><option value="weight_desc">Gewicht (Schwer->Leicht)</option><option value="quantity_desc">Anzahl (Viele->Wenige)</option></select></div>
</div>
</div>
<div class="card-body pane-content" id="lager-container"></div>
<div class="card-body pane-content flex-grow-1" id="lager-container" style="overflow-y: auto;"></div>
</div>
</div>
@@ -267,11 +267,11 @@ $conn->close();
<!-- TISCH -->
<div class="<?php echo $col_class_table; ?> h-100" id="pane-table">
<div class="card h-100 border-info">
<div class="card-header bg-info text-white">
<div class="card h-100 border-info d-flex flex-column shadow-sm">
<div class="card-header bg-info text-white flex-shrink-0">
<h5 class="mb-0"><i class="fas fa-table me-2"></i>Auf dem Tisch</h5>
</div>
<div class="card-body pane-content table-nested-sortable" id="table-container" data-carrier-id="null">
<div class="card-body pane-content table-nested-sortable flex-grow-1" id="table-container" data-carrier-id="null" style="overflow-y: auto;">
<!-- Items dropped here get carrier_id=null -->
</div>
</div>
@@ -279,11 +279,11 @@ $conn->close();
<!-- RUCKSACK -->
<div class="<?php echo $col_class_rucksack; ?> h-100" id="pane-rucksack">
<div class="card h-100 border-success">
<div class="card-header bg-success text-white">
<div class="card h-100 border-success d-flex flex-column shadow-sm">
<div class="card-header bg-success text-white flex-shrink-0">
<h5 class="mb-0"><i class="fas fa-backpack me-2"></i>Rucksäcke</h5>
</div>
<div class="card-body pane-content" id="carriers-container"></div>
<div class="card-body pane-content flex-grow-1" id="carriers-container" style="overflow-y: auto;"></div>
</div>
</div>
</div>

View File

@@ -135,7 +135,7 @@ $active_list_id = isset($_GET['list_id']) ? intval($_GET['list_id']) : (!empty($
<h5 class="mb-0 py-2"><i class="fas fa-tasks me-2 text-muted"></i><?php echo htmlspecialchars($active_list_name); ?></h5>
</div>
<div class="card-body p-4">
<ul class="list-group list-group-flush mb-4">
<ul class="list-group shadow-sm mb-4">
<?php foreach ($items as $item): ?>
<li class="list-group-item d-flex justify-content-between align-items-center py-3 px-2 border-bottom">
<form method="post" style="display:inline; margin:0;" class="flex-grow-1">
@@ -188,4 +188,7 @@ require_once 'footer.php';
if (isset($conn) && $conn instanceof mysqli) {
$conn->close();
}
?>isset($conn) && $conn instanceof mysqli) {
$conn->close();
}
?>