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
All checks were successful
Docker Build & Push / build-and-push (push) Successful in 36s
This commit is contained in:
@@ -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'; ?>
|
||||
@@ -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>
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
?>
|
||||
Reference in New Issue
Block a user