Refactor: Flat directory structure, removed Docker files from git, updated DB connection path
@@ -54,7 +54,7 @@ function get_available_backpacks_for_user($conn, $target_user_id, $household_id)
|
||||
}
|
||||
|
||||
$bps = [];
|
||||
$sql_bp = "SELECT id, name, user_id FROM backpacks WHERE user_id = ?";
|
||||
$sql_bp = "SELECT id, name, user_id, image_url FROM backpacks WHERE user_id = ?";
|
||||
if ($household_id) {
|
||||
$sql_bp .= " OR household_id = ?";
|
||||
$stmt_bp = $conn->prepare($sql_bp);
|
||||
@@ -95,6 +95,12 @@ while ($row = $result->fetch_assoc()) {
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
|
||||
<?php if(!empty($bp['image_url'])): ?>
|
||||
<div class="mb-3 text-center" style="height: 150px; overflow: hidden; background-color: #f8f9fa; border-radius: 5px; display: flex; align-items: center; justify-content: center;">
|
||||
<img src="<?php echo htmlspecialchars($bp['image_url']); ?>" alt="Rucksackbild" style="max-width: 100%; max-height: 100%; object-fit: contain;">
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<p class="card-text text-muted small mb-2">
|
||||
<?php echo htmlspecialchars($bp['manufacturer'] . ' ' . $bp['model']); ?>
|
||||
</p>
|
||||
@@ -4,7 +4,7 @@
|
||||
// NEU: Lade Zugangsdaten aus einer sicheren Konfigurationsdatei.
|
||||
// Diese Datei sollte außerhalb deines öffentlichen Web-Verzeichnisses liegen!
|
||||
// z.B. in /var/www/config/packliste.ini
|
||||
$config_path = __DIR__ . '/../config.ini'; // Annahme: die Datei liegt ein Verzeichnis höher
|
||||
$config_path = __DIR__ . '/config.ini'; // Pfad für flache Struktur angepasst
|
||||
|
||||
if (!file_exists($config_path)) {
|
||||
die("Kritischer Fehler: Die Konfigurationsdatei wurde nicht gefunden. Bitte erstellen Sie die 'config.ini'.");
|
||||
394
edit_backpack.php
Executable file
@@ -0,0 +1,394 @@
|
||||
<?php
|
||||
// Check for POST size limit exceeded immediately
|
||||
if ($_SERVER['REQUEST_METHOD'] == 'POST' && empty($_POST) && empty($_FILES) && $_SERVER['CONTENT_LENGTH'] > 0) {
|
||||
$max_size = ini_get('post_max_size');
|
||||
die("Fehler: Die hochgeladene Datei überschreitet das Server-Limit von $max_size. Bitte wählen Sie ein kleineres Bild.");
|
||||
}
|
||||
|
||||
// edit_backpack.php - Erstellen und Bearbeiten von Rucksäcken und Fächern
|
||||
$page_title = "Rucksack bearbeiten";
|
||||
require_once 'db_connect.php';
|
||||
|
||||
if (session_status() == PHP_SESSION_NONE) {
|
||||
session_start();
|
||||
}
|
||||
if (!isset($_SESSION['user_id'])) {
|
||||
header("Location: login.php");
|
||||
exit;
|
||||
}
|
||||
|
||||
$user_id = $_SESSION['user_id'];
|
||||
$backpack_id = isset($_GET['id']) ? intval($_GET['id']) : 0;
|
||||
$backpack = null;
|
||||
$compartments = [];
|
||||
$message = '';
|
||||
$image_url = '';
|
||||
|
||||
// Check Household
|
||||
$household_id = null;
|
||||
$stmt_hh = $conn->prepare("SELECT household_id FROM users WHERE id = ?");
|
||||
$stmt_hh->bind_param("i", $user_id);
|
||||
$stmt_hh->execute();
|
||||
$res_hh = $stmt_hh->get_result();
|
||||
if ($row = $res_hh->fetch_assoc()) {
|
||||
$household_id = $row['household_id'];
|
||||
}
|
||||
|
||||
// Image Upload Config
|
||||
$upload_dir = 'uploads/images/';
|
||||
if (!is_dir($upload_dir)) { @mkdir($upload_dir, 0777, true); }
|
||||
|
||||
function save_image_from_url($url, $upload_dir) {
|
||||
$context_options = ["http" => ["header" => "User-Agent: Mozilla/5.0\r\n", "timeout" => 10]];
|
||||
$context = stream_context_create($context_options);
|
||||
$image_data = @file_get_contents($url, false, $context);
|
||||
if ($image_data === false) return [false, "Bild-Download fehlgeschlagen."];
|
||||
$image_info = @getimagesizefromstring($image_data);
|
||||
if ($image_info === false) return [false, "Ungültiges Bild."];
|
||||
$allowed = ['image/jpeg', 'image/png', 'image/gif', 'image/webp'];
|
||||
if (!in_array($image_info['mime'], $allowed)) return [false, "Format nicht unterstützt."];
|
||||
$ext = image_type_to_extension($image_info[2], false);
|
||||
$name = uniqid('bp_url_', true) . '.' . $ext;
|
||||
if (file_put_contents($upload_dir . $name, $image_data)) return [true, $upload_dir . $name];
|
||||
return [false, "Speicherfehler."];
|
||||
}
|
||||
|
||||
function save_image_from_base64($base64_string, $upload_dir) {
|
||||
if (preg_match('/^data:image\/(\w+);base64,/', $base64_string, $type)) {
|
||||
$data = substr($base64_string, strpos($base64_string, ',') + 1);
|
||||
$type = strtolower($type[1]);
|
||||
if (!in_array($type, ['jpg', 'jpeg', 'png', 'gif'])) return [false, "Format nicht unterstützt."];
|
||||
$data = base64_decode($data);
|
||||
if ($data === false) return [false, "Dekodierfehler."];
|
||||
$name = uniqid('bp_paste_', true) . '.' . $type;
|
||||
if (file_put_contents($upload_dir . $name, $data)) return [true, $upload_dir . $name];
|
||||
}
|
||||
return [false, "Ungültiges Base64."];
|
||||
}
|
||||
|
||||
// Load existing data
|
||||
if ($backpack_id > 0) {
|
||||
$stmt = $conn->prepare("SELECT * FROM backpacks WHERE id = ? AND user_id = ?");
|
||||
$stmt->bind_param("ii", $backpack_id, $user_id);
|
||||
$stmt->execute();
|
||||
$result = $stmt->get_result();
|
||||
if ($result->num_rows > 0) {
|
||||
$backpack = $result->fetch_assoc();
|
||||
$image_url = $backpack['image_url'];
|
||||
|
||||
// Load Compartments
|
||||
$stmt_c = $conn->prepare("SELECT * FROM backpack_compartments WHERE backpack_id = ? ORDER BY sort_order ASC");
|
||||
$stmt_c->bind_param("i", $backpack_id);
|
||||
$stmt_c->execute();
|
||||
$res_c = $stmt_c->get_result();
|
||||
while ($row = $res_c->fetch_assoc()) {
|
||||
$compartments[] = $row;
|
||||
}
|
||||
} else {
|
||||
$message = '<div class="alert alert-danger">Rucksack nicht gefunden oder keine Berechtigung.</div>';
|
||||
$backpack_id = 0; // Reset to create mode
|
||||
}
|
||||
}
|
||||
|
||||
// Handle Form Submission BEFORE loading header
|
||||
if ($_SERVER['REQUEST_METHOD'] == 'POST') {
|
||||
$name = trim($_POST['name']);
|
||||
$manufacturer = trim($_POST['manufacturer']);
|
||||
$model = trim($_POST['model']);
|
||||
$weight = intval($_POST['weight_grams']);
|
||||
$volume = intval($_POST['volume_liters']);
|
||||
$share_household = isset($_POST['share_household']) ? 1 : 0;
|
||||
|
||||
// Image Handling
|
||||
$image_url_for_db = $image_url; // Keep existing by default
|
||||
$pasted_image = $_POST['pasted_image_data'] ?? '';
|
||||
$url_image = trim($_POST['image_url_input'] ?? '');
|
||||
|
||||
if (!empty($pasted_image)) {
|
||||
list($ok, $res) = save_image_from_base64($pasted_image, $upload_dir);
|
||||
if ($ok) {
|
||||
$image_url_for_db = $res;
|
||||
} else {
|
||||
$message .= '<div class="alert alert-warning">Fehler beim Speichern des eingefügten Bildes: ' . htmlspecialchars($res) . '</div>';
|
||||
}
|
||||
} elseif (isset($_FILES['image_file']) && $_FILES['image_file']['error'] != UPLOAD_ERR_NO_FILE) {
|
||||
// User attempted to upload a file
|
||||
if ($_FILES['image_file']['error'] == UPLOAD_ERR_OK) {
|
||||
$ext = strtolower(pathinfo($_FILES['image_file']['name'], PATHINFO_EXTENSION));
|
||||
$allowed_exts = ['jpg', 'jpeg', 'png', 'gif', 'webp'];
|
||||
if (in_array($ext, $allowed_exts)) {
|
||||
$name_file = uniqid('bp_img_', true) . '.' . $ext;
|
||||
if (move_uploaded_file($_FILES['image_file']['tmp_name'], $upload_dir . $name_file)) {
|
||||
$image_url_for_db = $upload_dir . $name_file;
|
||||
} else {
|
||||
$message .= '<div class="alert alert-danger">Fehler beim Verschieben der Datei. Schreibrechte prüfen.</div>';
|
||||
}
|
||||
} else {
|
||||
$message .= '<div class="alert alert-warning">Ungültiges Dateiformat. Erlaubt: JPG, PNG, GIF, WEBP.</div>';
|
||||
}
|
||||
} else {
|
||||
// Handle upload errors
|
||||
$err_code = $_FILES['image_file']['error'];
|
||||
$err_msg = 'Unbekannter Fehler';
|
||||
switch ($err_code) {
|
||||
case UPLOAD_ERR_INI_SIZE: $err_msg = 'Datei ist zu groß (php.ini limit).'; break;
|
||||
case UPLOAD_ERR_FORM_SIZE: $err_msg = 'Datei ist zu groß (HTML form limit).'; break;
|
||||
case UPLOAD_ERR_PARTIAL: $err_msg = 'Datei wurde nur teilweise hochgeladen.'; break;
|
||||
case UPLOAD_ERR_NO_TMP_DIR: $err_msg = 'Kein temporärer Ordner gefunden.'; break;
|
||||
case UPLOAD_ERR_CANT_WRITE: $err_msg = 'Fehler beim Schreiben auf die Festplatte.'; break;
|
||||
}
|
||||
$message .= '<div class="alert alert-danger">Upload-Fehler: ' . $err_msg . '</div>';
|
||||
}
|
||||
} elseif (!empty($url_image)) {
|
||||
list($ok, $res) = save_image_from_url($url_image, $upload_dir);
|
||||
if ($ok) {
|
||||
$image_url_for_db = $res;
|
||||
} else {
|
||||
$message .= '<div class="alert alert-warning">Fehler beim Laden von URL: ' . htmlspecialchars($res) . '</div>';
|
||||
}
|
||||
}
|
||||
|
||||
$final_household_id = ($share_household && $household_id) ? $household_id : NULL;
|
||||
|
||||
if ($backpack_id > 0) {
|
||||
// Update
|
||||
$stmt = $conn->prepare("UPDATE backpacks SET name=?, manufacturer=?, model=?, weight_grams=?, volume_liters=?, household_id=?, image_url=? WHERE id=? AND user_id=?");
|
||||
$stmt->bind_param("sssiissii", $name, $manufacturer, $model, $weight, $volume, $final_household_id, $image_url_for_db, $backpack_id, $user_id);
|
||||
$stmt->execute();
|
||||
} else {
|
||||
// Insert
|
||||
$stmt = $conn->prepare("INSERT INTO backpacks (user_id, household_id, name, manufacturer, model, weight_grams, volume_liters, image_url) VALUES (?, ?, ?, ?, ?, ?, ?, ?)");
|
||||
$stmt->bind_param("iisssiis", $user_id, $final_household_id, $name, $manufacturer, $model, $weight, $volume, $image_url_for_db);
|
||||
$stmt->execute();
|
||||
$backpack_id = $stmt->insert_id;
|
||||
}
|
||||
|
||||
// Handle Compartments
|
||||
if (isset($_POST['compartment_names'])) {
|
||||
$comp_names = $_POST['compartment_names'];
|
||||
$comp_ids = $_POST['compartment_ids'] ?? [];
|
||||
|
||||
// Get existing IDs to know what to delete
|
||||
$existing_ids = [];
|
||||
if($backpack_id > 0){
|
||||
$stmt_check = $conn->prepare("SELECT id FROM backpack_compartments WHERE backpack_id = ?");
|
||||
$stmt_check->bind_param("i", $backpack_id);
|
||||
$stmt_check->execute();
|
||||
$res_check = $stmt_check->get_result();
|
||||
while($row = $res_check->fetch_assoc()) $existing_ids[] = $row['id'];
|
||||
}
|
||||
|
||||
$kept_ids = [];
|
||||
|
||||
for ($i = 0; $i < count($comp_names); $i++) {
|
||||
$c_name = trim($comp_names[$i]);
|
||||
$c_id = intval($comp_ids[$i] ?? 0);
|
||||
|
||||
if (empty($c_name)) continue;
|
||||
|
||||
if ($c_id > 0 && in_array($c_id, $existing_ids)) {
|
||||
// Update
|
||||
$stmt_up = $conn->prepare("UPDATE backpack_compartments SET name = ?, sort_order = ? WHERE id = ?");
|
||||
$stmt_up->bind_param("sii", $c_name, $i, $c_id);
|
||||
$stmt_up->execute();
|
||||
$kept_ids[] = $c_id;
|
||||
} else {
|
||||
// Insert
|
||||
$stmt_in = $conn->prepare("INSERT INTO backpack_compartments (backpack_id, name, sort_order) VALUES (?, ?, ?)");
|
||||
$stmt_in->bind_param("isi", $backpack_id, $c_name, $i);
|
||||
$stmt_in->execute();
|
||||
}
|
||||
}
|
||||
|
||||
// Delete removed
|
||||
foreach ($existing_ids as $ex_id) {
|
||||
if (!in_array($ex_id, $kept_ids)) {
|
||||
$conn->query("DELETE FROM backpack_compartments WHERE id = $ex_id");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
header("Location: backpacks.php");
|
||||
exit;
|
||||
}
|
||||
|
||||
require_once 'header.php';
|
||||
?>
|
||||
|
||||
<div class="container mt-4">
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-lg-8">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h3 class="h5 mb-0"><?php echo $backpack_id > 0 ? 'Rucksack bearbeiten' : 'Neuen Rucksack anlegen'; ?></h3>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<?php echo $message; ?>
|
||||
|
||||
<form method="post" id="backpackForm" enctype="multipart/form-data">
|
||||
<input type="hidden" name="pasted_image_data" id="pasted_image_data">
|
||||
|
||||
<div class="row g-3 mb-4">
|
||||
<div class="col-md-8">
|
||||
<div class="row g-3">
|
||||
<div class="col-md-12">
|
||||
<label class="form-label">Bezeichnung (Spitzname)</label>
|
||||
<input type="text" name="name" class="form-control" required value="<?php echo htmlspecialchars($backpack['name'] ?? ''); ?>" placeholder="z.B. Mein großer Trekker">
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<label class="form-label">Hersteller</label>
|
||||
<input type="text" name="manufacturer" class="form-control" value="<?php echo htmlspecialchars($backpack['manufacturer'] ?? ''); ?>">
|
||||
</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'] ?? ''); ?>">
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<label class="form-label">Leergewicht (g)</label>
|
||||
<input type="number" name="weight_grams" class="form-control" value="<?php echo htmlspecialchars($backpack['weight_grams'] ?? 0); ?>">
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<label class="form-label">Volumen (Liter)</label>
|
||||
<input type="number" name="volume_liters" class="form-control" value="<?php echo htmlspecialchars($backpack['volume_liters'] ?? 0); ?>">
|
||||
</div>
|
||||
<div class="col-md-12">
|
||||
<div class="form-check form-switch">
|
||||
<input class="form-check-input" type="checkbox" id="share_household" name="share_household" <?php echo (!empty($backpack['household_id']) || $backpack_id == 0) ? 'checked' : ''; ?>>
|
||||
<label class="form-check-label" for="share_household">Mit Haushalt teilen</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<label class="form-label">Bild</label>
|
||||
<div class="card bg-light mb-2">
|
||||
<div class="card-body text-center p-2">
|
||||
<img id="imagePreview" src="<?php echo !empty($image_url) ? htmlspecialchars($image_url) : 'keinbild.png'; ?>" class="img-fluid" style="max-height:150px; object-fit:contain;">
|
||||
</div>
|
||||
</div>
|
||||
<input type="file" name="image_file" id="image_file" class="form-control form-control-sm mb-2" accept="image/*">
|
||||
<input type="text" name="image_url_input" id="image_url_input" class="form-control form-control-sm mb-2" placeholder="oder Bild-URL">
|
||||
<div id="pasteArea" class="paste-area small py-2" style="border: 2px dashed #ccc; text-align:center; cursor:pointer;">Bild einfügen (Strg+V)</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h5 class="border-bottom pb-2 mb-3">Fächeraufteilung</h5>
|
||||
<div class="alert alert-light small">
|
||||
Definiere hier die Bereiche deines Rucksacks (z.B. Deckelfach, Bodenfach). Diese erscheinen später in der Packliste als Container.
|
||||
</div>
|
||||
|
||||
<div id="compartments-container">
|
||||
<?php if (empty($compartments)): ?>
|
||||
<!-- Default Compartments for new backpack -->
|
||||
<div class="input-group mb-2 compartment-row">
|
||||
<span class="input-group-text"><i class="fas fa-grip-lines text-muted"></i></span>
|
||||
<input type="text" name="compartment_names[]" class="form-control" value="Hauptfach" placeholder="Fachname">
|
||||
<input type="hidden" name="compartment_ids[]" value="0">
|
||||
<button type="button" class="btn btn-outline-danger btn-remove-comp"><i class="fas fa-times"></i></button>
|
||||
</div>
|
||||
<div class="input-group mb-2 compartment-row">
|
||||
<span class="input-group-text"><i class="fas fa-grip-lines text-muted"></i></span>
|
||||
<input type="text" name="compartment_names[]" class="form-control" value="Deckelfach" placeholder="Fachname">
|
||||
<input type="hidden" name="compartment_ids[]" value="0">
|
||||
<button type="button" class="btn btn-outline-danger btn-remove-comp"><i class="fas fa-times"></i></button>
|
||||
</div>
|
||||
<?php else: ?>
|
||||
<?php foreach ($compartments as $comp):
|
||||
// Ensure proper escaping for HTML attributes
|
||||
$comp_name_escaped = htmlspecialchars($comp['name']);
|
||||
$comp_id_escaped = htmlspecialchars($comp['id']);
|
||||
?>
|
||||
<div class="input-group mb-2 compartment-row">
|
||||
<span class="input-group-text"><i class="fas fa-grip-lines text-muted"></i></span>
|
||||
<input type="text" name="compartment_names[]" class="form-control" value="<?php echo $comp_name_escaped; ?>">
|
||||
<input type="hidden" name="compartment_ids[]" value="<?php echo $comp_id_escaped; ?>">
|
||||
<button type="button" class="btn btn-outline-danger btn-remove-comp"><i class="fas fa-times"></i></button>
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
|
||||
<button type="button" class="btn btn-sm btn-outline-primary mb-4" id="add-compartment"><i class="fas fa-plus"></i> Fach hinzufügen</button>
|
||||
|
||||
<div class="d-flex justify-content-between border-top pt-3">
|
||||
<a href="backpacks.php" class="btn btn-secondary">Abbrechen</a>
|
||||
<button type="submit" class="btn btn-primary"><i class="fas fa-save me-2"></i>Speichern</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/Sortable/1.14.0/Sortable.min.js"></script>
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
const container = document.getElementById('compartments-container');
|
||||
|
||||
// Sortable for Compartments
|
||||
new Sortable(container, {
|
||||
handle: '.input-group-text',
|
||||
animation: 150
|
||||
});
|
||||
|
||||
document.getElementById('add-compartment').addEventListener('click', function() {
|
||||
const div = document.createElement('div');
|
||||
div.className = 'input-group mb-2 compartment-row';
|
||||
div.innerHTML = `
|
||||
<span class="input-group-text"><i class="fas fa-grip-lines text-muted"></i></span>
|
||||
<input type="text" name="compartment_names[]" class="form-control" placeholder="Fachname">
|
||||
<input type="hidden" name="compartment_ids[]" value="0">
|
||||
<button type="button" class="btn btn-outline-danger btn-remove-comp"><i class="fas fa-times"></i></button>
|
||||
`;
|
||||
container.appendChild(div);
|
||||
});
|
||||
|
||||
container.addEventListener('click', function(e) {
|
||||
if (e.target.closest('.btn-remove-comp')) {
|
||||
e.target.closest('.compartment-row').remove();
|
||||
}
|
||||
});
|
||||
|
||||
// Image Handling Logic (copied from add_article.php pattern)
|
||||
const imageFileInput = document.getElementById('image_file');
|
||||
const imagePreview = document.getElementById('imagePreview');
|
||||
const pasteArea = document.getElementById('pasteArea');
|
||||
const pastedImageDataInput = document.getElementById('pasted_image_data');
|
||||
|
||||
if(imageFileInput) {
|
||||
imageFileInput.addEventListener('change', function() {
|
||||
if (this.files && this.files[0]) {
|
||||
const reader = new FileReader();
|
||||
reader.onload = function(e) { imagePreview.src = e.target.result; }
|
||||
reader.readAsDataURL(this.files[0]);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (pasteArea) {
|
||||
pasteArea.addEventListener('paste', function(e) {
|
||||
e.preventDefault();
|
||||
const items = (e.clipboardData || window.clipboardData).items;
|
||||
for (const item of items) {
|
||||
if (item.type.indexOf('image') === 0) {
|
||||
const blob = item.getAsFile();
|
||||
const reader = new FileReader();
|
||||
reader.onload = function(event) {
|
||||
imagePreview.src = event.target.result;
|
||||
pastedImageDataInput.value = event.target.result;
|
||||
pasteArea.innerText = "Bild eingefügt!";
|
||||
pasteArea.style.borderColor = "green";
|
||||
};
|
||||
reader.readAsDataURL(blob);
|
||||
}
|
||||
}
|
||||
});
|
||||
// Add focus to paste area on click so it can receive paste events
|
||||
pasteArea.setAttribute('tabindex', '0');
|
||||
pasteArea.addEventListener('click', () => pasteArea.focus());
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<?php require_once 'footer.php'; ?>
|
||||
@@ -186,31 +186,56 @@ if ($_SERVER["REQUEST_METHOD"] == "POST" && $can_edit) {
|
||||
<p class="small text-muted">Wähle hier, wer welchen Rucksack trägt. Bereits vergebene Rucksäcke werden ausgeblendet.</p>
|
||||
|
||||
<?php
|
||||
// Calculate used backpacks (except for the user's own current assignment)
|
||||
// But we iterate users. So for each user loop, we need to know what OTHERS have assigned.
|
||||
// Logic: Get all values from $current_assignments.
|
||||
// Calculate used backpacks
|
||||
$all_assigned_backpack_ids = array_values($current_assignments);
|
||||
|
||||
// Prepare data for JS
|
||||
$user_backpacks_json = [];
|
||||
|
||||
foreach ($available_users as $user):
|
||||
$user_backpacks = get_available_backpacks_for_user($conn, $user['id'], $packing_list['household_id']);
|
||||
$my_current_bp_id = $current_assignments[$user['id']] ?? 0;
|
||||
|
||||
// Store for JS
|
||||
$user_backpacks_json[$user['id']] = [
|
||||
'current_id' => $my_current_bp_id,
|
||||
'backpacks' => $user_backpacks
|
||||
];
|
||||
|
||||
// Find current backpack details for display
|
||||
$current_bp_details = null;
|
||||
foreach ($user_backpacks as $bp) {
|
||||
if ($bp['id'] == $my_current_bp_id) {
|
||||
$current_bp_details = $bp;
|
||||
break;
|
||||
}
|
||||
}
|
||||
?>
|
||||
<div class="mb-3">
|
||||
<label class="form-label fw-bold"><?php echo htmlspecialchars($user['username']); ?></label>
|
||||
<select class="form-select form-select-sm" name="backpacks[<?php echo $user['id']; ?>]">
|
||||
<option value="0">-- Kein Rucksack --</option>
|
||||
<?php foreach ($user_backpacks as $bp):
|
||||
// FILTER: If backpack is used by someone else, skip it.
|
||||
// "Used by someone else" means: ID is in $all_assigned_backpack_ids AND ID != $my_current_bp_id
|
||||
if (in_array($bp['id'], $all_assigned_backpack_ids) && $bp['id'] != $my_current_bp_id) {
|
||||
continue;
|
||||
}
|
||||
?>
|
||||
<option value="<?php echo $bp['id']; ?>" <?php echo ($my_current_bp_id == $bp['id']) ? 'selected' : ''; ?>>
|
||||
<?php echo htmlspecialchars($bp['name']); ?>
|
||||
</option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
<div class="mb-4 border-bottom pb-3">
|
||||
<label class="form-label fw-bold mb-2"><?php echo htmlspecialchars($user['username']); ?></label>
|
||||
<input type="hidden" name="backpacks[<?php echo $user['id']; ?>]" id="input_bp_<?php echo $user['id']; ?>" value="<?php echo $my_current_bp_id; ?>">
|
||||
|
||||
<div class="d-flex align-items-center">
|
||||
<!-- Selected Backpack Display -->
|
||||
<div id="display_bp_<?php echo $user['id']; ?>" class="flex-grow-1 p-2 border rounded bg-white d-flex align-items-center" style="min-height: 60px;">
|
||||
<?php if ($current_bp_details): ?>
|
||||
<!-- Image if available -->
|
||||
<?php if(!empty($current_bp_details['image_url'])): ?>
|
||||
<img src="<?php echo htmlspecialchars($current_bp_details['image_url']); ?>" style="width: 40px; height: 40px; object-fit: cover; border-radius: 4px;" class="me-2">
|
||||
<?php else: ?>
|
||||
<i class="fas fa-hiking fa-2x text-muted me-2"></i>
|
||||
<?php endif; ?>
|
||||
<div>
|
||||
<div class="fw-bold small"><?php echo htmlspecialchars($current_bp_details['name']); ?></div>
|
||||
</div>
|
||||
<?php else: ?>
|
||||
<span class="text-muted small fst-italic">Kein Rucksack zugewiesen</span>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
<button type="button" class="btn btn-sm btn-outline-primary ms-2" onclick="openBackpackModal(<?php echo $user['id']; ?>, '<?php echo htmlspecialchars($user['username']); ?>')">
|
||||
<i class="fas fa-exchange-alt"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
@@ -229,4 +254,116 @@ if ($_SERVER["REQUEST_METHOD"] == "POST" && $can_edit) {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php require_once 'footer.php'; ?>
|
||||
<!-- Generic Modal -->
|
||||
<div class="modal fade" id="genericBpModal" tabindex="-1" aria-hidden="true">
|
||||
<div class="modal-dialog modal-dialog-centered modal-lg">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title" id="genericBpModalTitle">Rucksack wählen</h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||
</div>
|
||||
<div class="modal-body bg-light" id="genericBpModalBody">
|
||||
<!-- Content loaded via JS -->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
const userBackpacksData = <?php echo json_encode($user_backpacks_json); ?>;
|
||||
const allAssignedIds = <?php echo json_encode($all_assigned_backpack_ids); ?>;
|
||||
|
||||
function openBackpackModal(userId, username) {
|
||||
const data = userBackpacksData[userId];
|
||||
const currentBpId = data.current_id;
|
||||
const titleEl = document.getElementById('genericBpModalTitle');
|
||||
const bodyEl = document.getElementById('genericBpModalBody');
|
||||
|
||||
titleEl.textContent = 'Rucksack für ' + username + ' wählen';
|
||||
|
||||
let html = '<div class="row g-3">';
|
||||
|
||||
// Option: None
|
||||
html += `
|
||||
<div class="col-md-4 col-6">
|
||||
<div class="card h-100 text-center p-3 bp-select-card ${currentBpId == 0 ? 'border-primary bg-white' : ''}" onclick="selectBackpack(${userId}, 0)">
|
||||
<div class="card-body">
|
||||
<i class="fas fa-times-circle fa-2x text-muted mb-2"></i>
|
||||
<div class="small fw-bold">Kein Rucksack</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
data.backpacks.forEach(bp => {
|
||||
// Filter logic
|
||||
const isAssigned = allAssignedIds.some(id => String(id) === String(bp.id));
|
||||
if (isAssigned && String(bp.id) !== String(currentBpId)) return;
|
||||
|
||||
const imgHtml = bp.image_url ? `<img src="${bp.image_url}" class="card-img-top" style="height: 100px; object-fit: contain; padding: 10px;">` : `<div class="text-center py-4"><i class="fas fa-hiking fa-3x text-muted"></i></div>`;
|
||||
const activeClass = (String(bp.id) === String(currentBpId)) ? 'border-primary bg-white' : '';
|
||||
|
||||
html += `
|
||||
<div class="col-md-4 col-6">
|
||||
<div class="card h-100 bp-select-card ${activeClass}" onclick='selectBackpack(${userId}, ${bp.id})'>
|
||||
${imgHtml}
|
||||
<div class="card-body p-2 text-center">
|
||||
<div class="small fw-bold text-truncate">${bp.name}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
});
|
||||
|
||||
html += '</div>';
|
||||
bodyEl.innerHTML = html;
|
||||
|
||||
new bootstrap.Modal(document.getElementById('genericBpModal')).show();
|
||||
}
|
||||
|
||||
function selectBackpack(userId, bpId) {
|
||||
// Update Hidden Input
|
||||
document.getElementById('input_bp_' + userId).value = bpId;
|
||||
|
||||
// Find BP Data for display
|
||||
let bpName = 'Kein Rucksack zugewiesen';
|
||||
let bpImage = null;
|
||||
|
||||
if (bpId > 0) {
|
||||
const data = userBackpacksData[userId];
|
||||
const bp = data.backpacks.find(b => String(b.id) === String(bpId));
|
||||
if (bp) {
|
||||
bpName = bp.name;
|
||||
bpImage = bp.image_url;
|
||||
}
|
||||
}
|
||||
|
||||
// Update Display
|
||||
const displayDiv = document.getElementById('display_bp_' + userId);
|
||||
let html = '';
|
||||
if (bpId == 0) {
|
||||
html = '<span class="text-muted small fst-italic">Kein Rucksack zugewiesen</span>';
|
||||
} else {
|
||||
if (bpImage) {
|
||||
html += '<img src="' + bpImage + '" style="width: 40px; height: 40px; object-fit: cover; border-radius: 4px;" class="me-2">';
|
||||
} else {
|
||||
html += '<i class="fas fa-hiking fa-2x text-muted me-2"></i>';
|
||||
}
|
||||
html += '<div><div class="fw-bold small">' + bpName + '</div></div>';
|
||||
}
|
||||
displayDiv.innerHTML = html;
|
||||
|
||||
// Close Modal
|
||||
const modalEl = document.getElementById('genericBpModal');
|
||||
const modal = bootstrap.Modal.getInstance(modalEl);
|
||||
modal.hide();
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.bp-select-card { cursor: pointer; transition: transform 0.2s, box-shadow 0.2s; border: 1px solid rgba(0,0,0,0.1); }
|
||||
.bp-select-card:hover { transform: translateY(-3px); box-shadow: 0 5px 15px rgba(0,0,0,0.1); border-color: var(--color-primary); }
|
||||
</style>
|
||||
|
||||
<?php require_once 'footer.php'; ?>
|
||||
</replace>
|
||||
|
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 24 KiB |
|
Before Width: | Height: | Size: 3.4 MiB After Width: | Height: | Size: 3.4 MiB |
@@ -236,7 +236,7 @@ function render_print_table_rows($items, $level = 0) {
|
||||
<a href="manage_packing_list_items.php?id=<?php echo $packing_list_id; ?>" class="btn btn-outline-light btn-sm"><i class="fas fa-edit me-2"></i>Bearbeiten</a>
|
||||
<?php endif; ?>
|
||||
<a href="packing_lists.php" class="btn btn-outline-light btn-sm ms-2"><i class="fas fa-arrow-left me-2"></i>Zur Übersicht</a>
|
||||
<button onclick="window.print();" class="btn btn-outline-light btn-sm ms-2"><i class="fas fa-print me-2"></i>Drucken</button>
|
||||
<a href="print_packing_list.php?id=<?php echo $packing_list_id; ?>" target="_blank" class="btn btn-outline-light btn-sm ms-2"><i class="fas fa-print me-2"></i>Drucken</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -336,26 +336,6 @@ function render_print_table_rows($items, $level = 0) {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="print-view">
|
||||
<!-- Fallback Print View inside Details Page (usually hidden by CSS unless printing page directly) -->
|
||||
<div class="print-header">
|
||||
<h1><?php echo htmlspecialchars($packing_list['name']); ?></h1>
|
||||
</div>
|
||||
<table class="print-table" style="width:100%; border-collapse:collapse;">
|
||||
<thead><tr style="border-bottom:2px solid black;"><th>Artikel</th><th>Anzahl</th><th style="text-align:right;">Gewicht</th></tr></thead>
|
||||
<tbody>
|
||||
<?php
|
||||
// Simple flat print fallback
|
||||
if(isset($items_by_parent[0])) {
|
||||
foreach($items_by_parent[0] as $root) {
|
||||
echo '<tr><td>' . htmlspecialchars($root['article_name']) . '</td><td>' . $root['quantity'] . '</td><td style="text-align:right;">' . $root['weight_grams'] . '</td></tr>';
|
||||
}
|
||||
}
|
||||
?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div id="image-preview-tooltip"></div>
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
225
print_packing_list.php
Normal file
@@ -0,0 +1,225 @@
|
||||
<?php
|
||||
// print_packing_list.php - Eigene Seite für die Druckansicht einer Packliste
|
||||
|
||||
if (session_status() == PHP_SESSION_NONE) {
|
||||
session_start();
|
||||
}
|
||||
|
||||
if (!isset($_SESSION['user_id'])) {
|
||||
header("Location: login.php");
|
||||
exit;
|
||||
}
|
||||
|
||||
require_once 'db_connect.php';
|
||||
|
||||
$current_user_id = $_SESSION['user_id'];
|
||||
$packing_list_id = isset($_GET['id']) ? intval($_GET['id']) : 0;
|
||||
$packing_list = null;
|
||||
$items_by_parent = [];
|
||||
$message = '';
|
||||
|
||||
$total_weight_grams = 0;
|
||||
$weight_by_category = [];
|
||||
$total_consumable_weight = 0;
|
||||
|
||||
if ($packing_list_id > 0) {
|
||||
$stmt = $conn->prepare("SELECT id, name, description, user_id FROM packing_lists WHERE id = ?");
|
||||
if ($stmt) {
|
||||
$stmt->bind_param("i", $packing_list_id);
|
||||
$stmt->execute();
|
||||
$result = $stmt->get_result();
|
||||
if ($result->num_rows == 1) {
|
||||
$packing_list = $result->fetch_assoc();
|
||||
// Permission check logic could be expanded here
|
||||
} else {
|
||||
$message = 'Packliste nicht gefunden.';
|
||||
}
|
||||
$stmt->close();
|
||||
}
|
||||
} else {
|
||||
$message = 'Keine ID.';
|
||||
}
|
||||
|
||||
if ($packing_list) {
|
||||
// Artikel abrufen - LEFT JOIN für Container
|
||||
// WICHTIG: LEFT JOIN users für Trägernamen
|
||||
$sql = "SELECT
|
||||
pli.id AS packing_list_item_id, pli.article_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, 'Unbekannt') AS name,
|
||||
a.weight_grams, c.name AS category_name, a.consumable, a.image_url,
|
||||
u.username AS carrier_name
|
||||
FROM packing_list_items pli
|
||||
LEFT JOIN articles a ON pli.article_id = a.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
|
||||
LEFT JOIN users u ON pli.carrier_user_id = u.id
|
||||
WHERE pli.packing_list_id = ?
|
||||
ORDER BY pli.order_index ASC";
|
||||
|
||||
$stmt = $conn->prepare($sql);
|
||||
if ($stmt) {
|
||||
$stmt->bind_param("i", $packing_list_id);
|
||||
$stmt->execute();
|
||||
$result = $stmt->get_result();
|
||||
|
||||
$raw_items = [];
|
||||
while ($row = $result->fetch_assoc()) {
|
||||
// Fix Name for Backpacks if needed
|
||||
if ($row['backpack_id'] && empty($row['name'])) $row['name'] = "Rucksack";
|
||||
|
||||
$raw_items[] = $row;
|
||||
|
||||
// Stats
|
||||
$w = ($row['weight_grams'] ?? 0) * $row['quantity'];
|
||||
$total_weight_grams += $w;
|
||||
if ($row['consumable']) $total_consumable_weight += $w;
|
||||
$cat = $row['category_name'] ?: 'Sonstiges';
|
||||
if (!isset($weight_by_category[$cat])) $weight_by_category[$cat] = 0;
|
||||
$weight_by_category[$cat] += $w;
|
||||
}
|
||||
$stmt->close();
|
||||
|
||||
// Build Tree
|
||||
$items_by_id = [];
|
||||
foreach ($raw_items as $item) {
|
||||
$item['children'] = [];
|
||||
$items_by_id[$item['packing_list_item_id']] = $item;
|
||||
}
|
||||
foreach ($items_by_id as $id => &$item) {
|
||||
$pid = $item['parent_packing_list_item_id'];
|
||||
if ($pid && isset($items_by_id[$pid])) {
|
||||
$items_by_id[$pid]['children'][] = &$item;
|
||||
}
|
||||
}
|
||||
unset($item);
|
||||
|
||||
foreach ($items_by_id as $item) {
|
||||
if (!$item['parent_packing_list_item_id']) {
|
||||
$items_by_parent[] = $item; // Root items
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
$conn->close();
|
||||
$total_weight_without_consumables = $total_weight_grams - $total_consumable_weight;
|
||||
|
||||
// Recursive Render Function for Table Rows
|
||||
function render_rows_recursive($items, $level = 0) {
|
||||
foreach ($items as $item) {
|
||||
$is_container = !empty($item['backpack_id']) || !empty($item['backpack_compartment_id']);
|
||||
$indent = $level * 20; // px
|
||||
|
||||
$row_style = "";
|
||||
$name_style = "";
|
||||
if (!empty($item['backpack_id'])) {
|
||||
$row_style = "font-weight:bold; background-color:#f0f0f0; border-top: 2px solid #000;";
|
||||
$name_style = "text-transform:uppercase;";
|
||||
} elseif (!empty($item['backpack_compartment_id'])) {
|
||||
$row_style = "font-weight:bold; background-color:#fafafa; border-bottom:1px solid #ccc;";
|
||||
$name_style = "font-style:italic;";
|
||||
}
|
||||
|
||||
echo '<tr style="' . $row_style . '">';
|
||||
|
||||
// Checkbox
|
||||
echo '<td class="print-checkbox-cell"><div class="print-checkbox" style="' . ($is_container ? 'visibility:hidden;' : '') . '"></div></td>';
|
||||
|
||||
// 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>';
|
||||
echo '</td>';
|
||||
|
||||
// Qty
|
||||
echo '<td class="text-center">' . ($is_container ? '' : $item['quantity']) . '</td>';
|
||||
|
||||
// Weight
|
||||
echo '<td class="text-end">' . ($item['weight_grams'] > 0 ? number_format($item['weight_grams'], 0, ',', '.') . ' g' : '') . '</td>';
|
||||
|
||||
echo '</tr>';
|
||||
|
||||
if (!empty($item['children'])) {
|
||||
render_rows_recursive($item['children'], $level + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
?>
|
||||
<!DOCTYPE html>
|
||||
<html lang="de">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title><?php echo htmlspecialchars($packing_list['name'] ?? 'Druckansicht'); ?></title>
|
||||
<!-- Updated Version Force -->
|
||||
<style>
|
||||
@media print {
|
||||
@page { size: A4; margin: 1.5cm; }
|
||||
body { font-family: sans-serif; font-size: 10pt; -webkit-print-color-adjust: exact !important; print-color-adjust: exact !important; }
|
||||
.no-print { display: none; }
|
||||
}
|
||||
body { font-family: sans-serif; color: #000; max-width: 900px; margin: 0 auto; padding: 20px; }
|
||||
h1 { text-align: center; margin-bottom: 5px; }
|
||||
p.desc { text-align: center; color: #666; margin-bottom: 30px; }
|
||||
|
||||
.carrier-section { margin-bottom: 30px; page-break-inside: avoid; }
|
||||
.carrier-title { font-size: 14pt; border-bottom: 2px solid #000; margin-bottom: 10px; padding-bottom: 5px; }
|
||||
|
||||
table { width: 100%; border-collapse: collapse; margin-bottom: 10px; }
|
||||
th { text-align: left; border-bottom: 1px solid #000; padding: 5px; font-size: 9pt; }
|
||||
td { padding: 4px 5px; border-bottom: 1px solid #eee; vertical-align: top; }
|
||||
|
||||
.text-center { text-align: center; }
|
||||
.text-end { text-align: right; }
|
||||
|
||||
.print-checkbox { width: 12px; height: 12px; border: 1px solid #000; display: inline-block; }
|
||||
.print-checkbox-cell { width: 20px; }
|
||||
|
||||
.stats { margin-top: 40px; border-top: 2px solid #000; padding-top: 10px; display: flex; flex-wrap: wrap; gap: 20px; }
|
||||
.stat-item { width: 45%; margin-bottom: 5px; border-bottom: 1px dotted #ccc; display: flex; justify-content: space-between; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<?php if ($packing_list): ?>
|
||||
<h1><?php echo htmlspecialchars($packing_list['name']); ?></h1>
|
||||
<p class="desc"><?php echo htmlspecialchars($packing_list['description'] ?? ''); ?></p>
|
||||
|
||||
<?php
|
||||
if (empty($items_by_parent)) {
|
||||
echo '<p style="text-align:center;">Keine Artikel.</p>';
|
||||
} else {
|
||||
// Group by Carrier
|
||||
$items_by_carrier = [];
|
||||
foreach ($items_by_parent as $root) {
|
||||
$c_name = $root['carrier_name'] ?: 'Sonstiges';
|
||||
$items_by_carrier[$c_name][] = $root;
|
||||
}
|
||||
|
||||
foreach ($items_by_carrier as $carrier => $roots) {
|
||||
echo '<div class="carrier-section">';
|
||||
echo '<div class="carrier-title">' . htmlspecialchars($carrier) . '</div>';
|
||||
echo '<table>';
|
||||
echo '<thead><tr><th style="width:20px;"></th><th>Artikel / Container</th><th class="text-center" style="width:50px;">Anz.</th><th class="text-end" style="width:80px;">Gewicht</th></tr></thead>';
|
||||
echo '<tbody>';
|
||||
render_rows_recursive($roots);
|
||||
echo '</tbody>';
|
||||
echo '</table>';
|
||||
echo '</div>';
|
||||
}
|
||||
}
|
||||
?>
|
||||
|
||||
<div class="stats">
|
||||
<div class="stat-item"><strong>Gesamtgewicht:</strong> <span><?php echo number_format($total_weight_grams, 0, ',', '.'); ?> g</span></div>
|
||||
<div class="stat-item"><strong>Basisgewicht (ohne Verbrauch):</strong> <span><?php echo number_format($total_weight_without_consumables, 0, ',', '.'); ?> g</span></div>
|
||||
<?php arsort($weight_by_category); foreach($weight_by_category as $cat => $w): ?>
|
||||
<div class="stat-item"><span><?php echo htmlspecialchars($cat); ?>:</span> <span><?php echo number_format($w, 0, ',', '.'); ?> g</span></div>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
|
||||
<script>window.print();</script>
|
||||
<?php else: ?>
|
||||
<p style="color:red; text-align:center;"><?php echo htmlspecialchars($message); ?></p>
|
||||
<?php endif; ?>
|
||||
</body>
|
||||
</html>
|
||||
|
Before Width: | Height: | Size: 22 KiB After Width: | Height: | Size: 22 KiB |
@@ -1,249 +0,0 @@
|
||||
<?php
|
||||
// edit_backpack.php - Erstellen und Bearbeiten von Rucksäcken und Fächern
|
||||
$page_title = "Rucksack bearbeiten";
|
||||
require_once 'db_connect.php';
|
||||
|
||||
if (session_status() == PHP_SESSION_NONE) {
|
||||
session_start();
|
||||
}
|
||||
if (!isset($_SESSION['user_id'])) {
|
||||
header("Location: login.php");
|
||||
exit;
|
||||
}
|
||||
|
||||
$user_id = $_SESSION['user_id'];
|
||||
$backpack_id = isset($_GET['id']) ? intval($_GET['id']) : 0;
|
||||
$backpack = null;
|
||||
$compartments = [];
|
||||
$message = '';
|
||||
|
||||
// Check Household
|
||||
$household_id = null;
|
||||
$stmt_hh = $conn->prepare("SELECT household_id FROM users WHERE id = ?");
|
||||
$stmt_hh->bind_param("i", $user_id);
|
||||
$stmt_hh->execute();
|
||||
$res_hh = $stmt_hh->get_result();
|
||||
if ($row = $res_hh->fetch_assoc()) {
|
||||
$household_id = $row['household_id'];
|
||||
}
|
||||
|
||||
// Handle Form Submission BEFORE loading header
|
||||
if ($_SERVER['REQUEST_METHOD'] == 'POST') {
|
||||
$name = trim($_POST['name']);
|
||||
$manufacturer = trim($_POST['manufacturer']);
|
||||
$model = trim($_POST['model']);
|
||||
$weight = intval($_POST['weight_grams']);
|
||||
$volume = intval($_POST['volume_liters']);
|
||||
$share_household = isset($_POST['share_household']) ? 1 : 0;
|
||||
|
||||
$final_household_id = ($share_household && $household_id) ? $household_id : NULL;
|
||||
|
||||
if ($backpack_id > 0) {
|
||||
// Update
|
||||
$stmt = $conn->prepare("UPDATE backpacks SET name=?, manufacturer=?, model=?, weight_grams=?, volume_liters=?, household_id=? WHERE id=? AND user_id=?");
|
||||
$stmt->bind_param("sssiisii", $name, $manufacturer, $model, $weight, $volume, $final_household_id, $backpack_id, $user_id);
|
||||
$stmt->execute();
|
||||
} else {
|
||||
// Insert
|
||||
$stmt = $conn->prepare("INSERT INTO backpacks (user_id, household_id, name, manufacturer, model, weight_grams, volume_liters) VALUES (?, ?, ?, ?, ?, ?, ?)");
|
||||
$stmt->bind_param("iisssii", $user_id, $final_household_id, $name, $manufacturer, $model, $weight, $volume);
|
||||
$stmt->execute();
|
||||
$backpack_id = $stmt->insert_id;
|
||||
}
|
||||
|
||||
// Handle Compartments
|
||||
if (isset($_POST['compartment_names'])) {
|
||||
$comp_names = $_POST['compartment_names'];
|
||||
$comp_ids = $_POST['compartment_ids'] ?? [];
|
||||
|
||||
// Get existing IDs to know what to delete
|
||||
// We need to re-fetch existing just for this logic if not loaded yet, or just assume based on IDs
|
||||
// Since we haven't loaded existing yet in this flow, let's just operate on IDs.
|
||||
|
||||
// First load existing IDs for safety to check ownership indirectly via backpack_id
|
||||
$existing_ids = [];
|
||||
$stmt_check = $conn->prepare("SELECT id FROM backpack_compartments WHERE backpack_id = ?");
|
||||
$stmt_check->bind_param("i", $backpack_id);
|
||||
$stmt_check->execute();
|
||||
$res_check = $stmt_check->get_result();
|
||||
while($row = $res_check->fetch_assoc()) $existing_ids[] = $row['id'];
|
||||
|
||||
$kept_ids = [];
|
||||
|
||||
for ($i = 0; $i < count($comp_names); $i++) {
|
||||
$c_name = trim($comp_names[$i]);
|
||||
$c_id = intval($comp_ids[$i] ?? 0);
|
||||
|
||||
if (empty($c_name)) continue;
|
||||
|
||||
if ($c_id > 0 && in_array($c_id, $existing_ids)) {
|
||||
// Update
|
||||
$stmt_up = $conn->prepare("UPDATE backpack_compartments SET name = ?, sort_order = ? WHERE id = ?");
|
||||
$stmt_up->bind_param("sii", $c_name, $i, $c_id);
|
||||
$stmt_up->execute();
|
||||
$kept_ids[] = $c_id;
|
||||
} else {
|
||||
// Insert
|
||||
$stmt_in = $conn->prepare("INSERT INTO backpack_compartments (backpack_id, name, sort_order) VALUES (?, ?, ?)");
|
||||
$stmt_in->bind_param("isi", $backpack_id, $c_name, $i);
|
||||
$stmt_in->execute();
|
||||
}
|
||||
}
|
||||
|
||||
// Delete removed
|
||||
foreach ($existing_ids as $ex_id) {
|
||||
if (!in_array($ex_id, $kept_ids)) {
|
||||
$conn->query("DELETE FROM backpack_compartments WHERE id = $ex_id");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
header("Location: backpacks.php");
|
||||
exit;
|
||||
}
|
||||
|
||||
require_once 'header.php';
|
||||
|
||||
// Load existing data (Moved after header inclusion is fine for display logic, but data loading could be before too)
|
||||
if ($backpack_id > 0) {
|
||||
$stmt = $conn->prepare("SELECT * FROM backpacks WHERE id = ? AND user_id = ?");
|
||||
$stmt->bind_param("ii", $backpack_id, $user_id);
|
||||
$stmt->execute();
|
||||
$result = $stmt->get_result();
|
||||
if ($result->num_rows > 0) {
|
||||
$backpack = $result->fetch_assoc();
|
||||
|
||||
// Load Compartments
|
||||
$stmt_c = $conn->prepare("SELECT * FROM backpack_compartments WHERE backpack_id = ? ORDER BY sort_order ASC");
|
||||
$stmt_c->bind_param("i", $backpack_id);
|
||||
$stmt_c->execute();
|
||||
$res_c = $stmt_c->get_result();
|
||||
while ($row = $res_c->fetch_assoc()) {
|
||||
$compartments[] = $row;
|
||||
}
|
||||
} else {
|
||||
$message = '<div class="alert alert-danger">Rucksack nicht gefunden oder keine Berechtigung.</div>';
|
||||
$backpack_id = 0; // Reset to create mode
|
||||
}
|
||||
}
|
||||
?>
|
||||
|
||||
?>
|
||||
|
||||
<div class="container mt-4">
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-lg-8">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h3 class="h5 mb-0"><?php echo $backpack_id > 0 ? 'Rucksack bearbeiten' : 'Neuen Rucksack anlegen'; ?></h3>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<?php echo $message; ?>
|
||||
|
||||
<form method="post" id="backpackForm">
|
||||
<div class="row g-3 mb-4">
|
||||
<div class="col-md-12">
|
||||
<label class="form-label">Bezeichnung (Spitzname)</label>
|
||||
<input type="text" name="name" class="form-control" required value="<?php echo htmlspecialchars($backpack['name'] ?? ''); ?>" placeholder="z.B. Mein großer Trekker">
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<label class="form-label">Hersteller</label>
|
||||
<input type="text" name="manufacturer" class="form-control" value="<?php echo htmlspecialchars($backpack['manufacturer'] ?? ''); ?>">
|
||||
</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'] ?? ''); ?>">
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<label class="form-label">Leergewicht (g)</label>
|
||||
<input type="number" name="weight_grams" class="form-control" value="<?php echo htmlspecialchars($backpack['weight_grams'] ?? 0); ?>">
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<label class="form-label">Volumen (Liter)</label>
|
||||
<input type="number" name="volume_liters" class="form-control" value="<?php echo htmlspecialchars($backpack['volume_liters'] ?? 0); ?>">
|
||||
</div>
|
||||
<div class="col-md-12">
|
||||
<div class="form-check form-switch">
|
||||
<input class="form-check-input" type="checkbox" id="share_household" name="share_household" <?php echo (!empty($backpack['household_id']) || $backpack_id == 0) ? 'checked' : ''; ?>>
|
||||
<label class="form-check-label" for="share_household">Mit Haushalt teilen</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h5 class="border-bottom pb-2 mb-3">Fächeraufteilung</h5>
|
||||
<div class="alert alert-light small">
|
||||
Definiere hier die Bereiche deines Rucksacks (z.B. Deckelfach, Bodenfach). Diese erscheinen später in der Packliste als Container.
|
||||
</div>
|
||||
|
||||
<div id="compartments-container">
|
||||
<?php if (empty($compartments)): ?>
|
||||
<!-- Default Compartments for new backpack -->
|
||||
<div class="input-group mb-2 compartment-row">
|
||||
<span class="input-group-text"><i class="fas fa-grip-lines text-muted"></i></span>
|
||||
<input type="text" name="compartment_names[]" class="form-control" value="Hauptfach" placeholder="Fachname">
|
||||
<input type="hidden" name="compartment_ids[]" value="0">
|
||||
<button type="button" class="btn btn-outline-danger btn-remove-comp"><i class="fas fa-times"></i></button>
|
||||
</div>
|
||||
<div class="input-group mb-2 compartment-row">
|
||||
<span class="input-group-text"><i class="fas fa-grip-lines text-muted"></i></span>
|
||||
<input type="text" name="compartment_names[]" class="form-control" value="Deckelfach" placeholder="Fachname">
|
||||
<input type="hidden" name="compartment_ids[]" value="0">
|
||||
<button type="button" class="btn btn-outline-danger btn-remove-comp"><i class="fas fa-times"></i></button>
|
||||
</div>
|
||||
<?php else: ?>
|
||||
<?php foreach ($compartments as $comp): ?>
|
||||
<div class="input-group mb-2 compartment-row">
|
||||
<span class="input-group-text"><i class="fas fa-grip-lines text-muted"></i></span>
|
||||
<input type="text" name="compartment_names[]" class="form-control" value="<?php echo htmlspecialchars($comp['name']); ?>">
|
||||
<input type="hidden" name="compartment_ids[]" value="<?php echo $comp['id']; ?>">
|
||||
<button type="button" class="btn btn-outline-danger btn-remove-comp"><i class="fas fa-times"></i></button>
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
|
||||
<button type="button" class="btn btn-sm btn-outline-primary mb-4" id="add-compartment"><i class="fas fa-plus"></i> Fach hinzufügen</button>
|
||||
|
||||
<div class="d-flex justify-content-between border-top pt-3">
|
||||
<a href="backpacks.php" class="btn btn-secondary">Abbrechen</a>
|
||||
<button type="submit" class="btn btn-primary"><i class="fas fa-save me-2"></i>Speichern</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/Sortable/1.14.0/Sortable.min.js"></script>
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
const container = document.getElementById('compartments-container');
|
||||
|
||||
// Sortable for Compartments
|
||||
new Sortable(container, {
|
||||
handle: '.input-group-text',
|
||||
animation: 150
|
||||
});
|
||||
|
||||
document.getElementById('add-compartment').addEventListener('click', function() {
|
||||
const div = document.createElement('div');
|
||||
div.className = 'input-group mb-2 compartment-row';
|
||||
div.innerHTML = `
|
||||
<span class="input-group-text"><i class="fas fa-grip-lines text-muted"></i></span>
|
||||
<input type="text" name="compartment_names[]" class="form-control" placeholder="Fachname">
|
||||
<input type="hidden" name="compartment_ids[]" value="0">
|
||||
<button type="button" class="btn btn-outline-danger btn-remove-comp"><i class="fas fa-times"></i></button>
|
||||
`;
|
||||
container.appendChild(div);
|
||||
});
|
||||
|
||||
container.addEventListener('click', function(e) {
|
||||
if (e.target.closest('.btn-remove-comp')) {
|
||||
e.target.closest('.compartment-row').remove();
|
||||
}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
<?php require_once 'footer.php'; ?>
|
||||
@@ -1,283 +0,0 @@
|
||||
<?php
|
||||
// print_packing_list.php - Eigene Seite für die Druckansicht einer Packliste
|
||||
|
||||
if (session_status() == PHP_SESSION_NONE) {
|
||||
session_start();
|
||||
}
|
||||
|
||||
// Prüfen, ob der Benutzer angemeldet ist
|
||||
if (!isset($_SESSION['user_id'])) {
|
||||
// Wenn nicht angemeldet, zur Login-Seite umleiten oder Fehlermeldung anzeigen
|
||||
// Für die Druckansicht leiten wir einfach weiter, da ein unautorisierter Druck keinen Sinn macht.
|
||||
header("Location: login.php");
|
||||
exit;
|
||||
}
|
||||
|
||||
require_once 'db_connect.php'; // Datenbankverbindung
|
||||
|
||||
$current_user_id = $_SESSION['user_id'];
|
||||
$packing_list_id = isset($_GET['id']) ? intval($_GET['id']) : 0;
|
||||
$packing_list = null;
|
||||
$packing_list_items = [];
|
||||
$message = '';
|
||||
|
||||
$total_weight_grams = 0;
|
||||
$weight_by_category = [];
|
||||
$total_consumable_weight = 0;
|
||||
|
||||
if ($packing_list_id > 0) {
|
||||
// Packliste abrufen
|
||||
$stmt = $conn->prepare("SELECT id, name, description, user_id FROM packing_lists WHERE id = ?");
|
||||
if ($stmt) {
|
||||
$stmt->bind_param("i", $packing_list_id);
|
||||
$stmt->execute();
|
||||
$result = $stmt->get_result();
|
||||
if ($result->num_rows == 1) {
|
||||
$packing_list = $result->fetch_assoc();
|
||||
// Nur eigene Packlisten oder globale Packlisten dürfen gedruckt werden
|
||||
if ($packing_list['user_id'] != $current_user_id) {
|
||||
$message = 'Sie sind nicht berechtigt, diese Packliste zu drucken.';
|
||||
$packing_list = null;
|
||||
}
|
||||
} else {
|
||||
$message = 'Packliste nicht gefunden.';
|
||||
}
|
||||
$stmt->close();
|
||||
} else {
|
||||
$message = 'Datenbankfehler beim Abrufen der Packliste.';
|
||||
}
|
||||
} else {
|
||||
$message = 'Keine Packlisten-ID zum Drucken angegeben.';
|
||||
}
|
||||
|
||||
if ($packing_list) {
|
||||
// Artikel der Packliste abrufen - LEFT JOIN für Container Support
|
||||
$stmt = $conn->prepare("SELECT pli.id AS packing_list_item_id, pli.article_id, pli.quantity, pli.parent_packing_list_item_id, pli.backpack_id, pli.backpack_compartment_id, COALESCE(a.name, pli.name) AS name, a.weight_grams, c.name AS category_name, a.consumable, a.image_url FROM packing_list_items pli LEFT JOIN articles a ON pli.article_id = a.id LEFT JOIN categories c ON a.category_id = c.id WHERE pli.packing_list_id = ? ORDER BY pli.parent_packing_list_item_id ASC, pli.order_index ASC");
|
||||
if ($stmt) {
|
||||
$stmt->bind_param("i", $packing_list_id);
|
||||
$stmt->execute();
|
||||
$result = $stmt->get_result();
|
||||
$raw_packing_list_items = [];
|
||||
while ($row = $result->fetch_assoc()) {
|
||||
$raw_packing_list_items[] = $row;
|
||||
$item_weight = $row['weight_grams'] ?? 0;
|
||||
$item_total_weight = $item_weight * $row['quantity'];
|
||||
$total_weight_grams += $item_total_weight;
|
||||
$category_name = $row['category_name'] ?: 'Unkategorisiert';
|
||||
if (!isset($weight_by_category[$category_name])) {
|
||||
$weight_by_category[$category_name] = 0;
|
||||
}
|
||||
$weight_by_category[$category_name] += $item_total_weight;
|
||||
if ($row['consumable']) {
|
||||
$total_consumable_weight += $item_total_weight;
|
||||
}
|
||||
}
|
||||
$stmt->close();
|
||||
|
||||
// Items in hierarchische Struktur bringen
|
||||
$items_by_id = [];
|
||||
foreach ($raw_packing_list_items as $item) {
|
||||
$item['children'] = [];
|
||||
$items_by_id[$item['packing_list_item_id']] = $item;
|
||||
}
|
||||
foreach ($items_by_id as $item_id => &$item) {
|
||||
if ($item['parent_packing_list_item_id'] !== null && isset($items_by_id[$item['parent_packing_list_item_id']])) {
|
||||
$items_by_id[$item['parent_packing_list_item_id']]['children'][] = &$items_by_id[$item_id];
|
||||
}
|
||||
}
|
||||
unset($item); // Wichtig, um Referenzfehler zu vermeiden!
|
||||
foreach ($items_by_id as $item) {
|
||||
if ($item['parent_packing_list_item_id'] === null) {
|
||||
$packing_list_items[] = $item;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$message = 'Datenbankfehler beim Abrufen der Packlistenartikel.';
|
||||
}
|
||||
}
|
||||
$conn->close();
|
||||
$total_weight_without_consumables = $total_weight_grams - $total_consumable_weight;
|
||||
|
||||
// Rekursive Funktion zur Darstellung der Artikel (für Druckansicht angepasst)
|
||||
function renderPrintablePackingListItemsRecursive($items, $level = 0) {
|
||||
$html = '<ul class="print-list-level level-' . $level . '">';
|
||||
foreach ($items as $item) {
|
||||
$item_weight = $item['weight_grams'] ?? 0;
|
||||
$item_total_weight_current = $item_weight * $item['quantity'];
|
||||
|
||||
// Container Styling
|
||||
$is_container = !empty($item['backpack_id']) || !empty($item['backpack_compartment_id']);
|
||||
$container_style = "";
|
||||
if (!empty($item['backpack_id'])) {
|
||||
$container_style = "font-weight: bold; font-size: 1.1em; margin-top: 10px; border-bottom: 2px solid #ccc;";
|
||||
} elseif (!empty($item['backpack_compartment_id'])) {
|
||||
$container_style = "font-weight: bold; margin-top: 5px; color: #555;";
|
||||
}
|
||||
|
||||
$details_string = '';
|
||||
if (!$is_container) {
|
||||
$details_string = '(' . number_format($item_weight, 0, ',', '.') . 'g' . ($item['category_name'] ? ', ' . htmlspecialchars($item['category_name']) : '') . ($item['consumable'] ? ', Verbrauchbar' : '') . ')';
|
||||
}
|
||||
|
||||
$indent_style = ($level > 0) ? 'padding-left: ' . ($level * 1.5) . 'em;' : '';
|
||||
|
||||
$html .= '
|
||||
<li class="print-list-item" style="' . $indent_style . ' ' . $container_style . '">
|
||||
<span class="print-checkbox" style="' . ($is_container ? 'visibility:hidden;' : '') . '"></span>
|
||||
<span class="print-quantity">' . ($is_container ? '' : htmlspecialchars($item['quantity']) . 'x') . '</span>
|
||||
<span class="print-name">' . htmlspecialchars($item['name']) . '</span>
|
||||
<span class="print-details">' . $details_string . '</span>
|
||||
<span class="print-total-weight">' . ($is_container ? '' : number_format($item_total_weight_current, 0, ',', '.') . 'g') . '</span>
|
||||
</li>';
|
||||
if (!empty($item['children'])) {
|
||||
$html .= renderPrintablePackingListItemsRecursive($item['children'], $level + 1);
|
||||
}
|
||||
}
|
||||
$html .= '</ul>';
|
||||
return $html;
|
||||
}
|
||||
?>
|
||||
<!DOCTYPE html>
|
||||
<html lang="de">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Packliste <?php echo htmlspecialchars($packing_list['name'] ?? 'Druckansicht'); ?></title>
|
||||
<!-- Spezifisches Stylesheet für die Druckansicht einbinden -->
|
||||
<link rel="stylesheet" href="print.css" media="print">
|
||||
<!-- Optional: Ein minimales Stylesheet für die Bildschirmanzeige dieser Seite, falls nötig -->
|
||||
<style>
|
||||
/* Grundlegende Styles für die Bildschirmanzeige dieser Druckseite */
|
||||
body {
|
||||
font-family: Arial, sans-serif;
|
||||
margin: 20px;
|
||||
color: #333;
|
||||
}
|
||||
.container-print {
|
||||
max-width: 800px;
|
||||
margin: 0 auto;
|
||||
padding: 20px;
|
||||
border: 1px solid #ccc; /* Minimaler Rahmen für die Bildschirmanzeige */
|
||||
box-shadow: 0 0 10px rgba(0,0,0,0.1);
|
||||
}
|
||||
h1, h2, h3 {
|
||||
text-align: center;
|
||||
color: #2c3e50;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
p.description {
|
||||
text-align: center;
|
||||
color: #777;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
.section-title {
|
||||
font-size: 1.2em;
|
||||
font-weight: bold;
|
||||
margin-top: 20px;
|
||||
margin-bottom: 10px;
|
||||
border-bottom: 1px solid #eee;
|
||||
padding-bottom: 5px;
|
||||
}
|
||||
.print-list-level {
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
.print-list-item {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: flex-start;
|
||||
padding: 5px 0;
|
||||
border-bottom: 1px dashed #eee;
|
||||
font-size: 0.9em;
|
||||
}
|
||||
.print-list-item:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
.print-checkbox {
|
||||
width: 15px;
|
||||
height: 15px;
|
||||
border: 1px solid #000;
|
||||
flex-shrink: 0;
|
||||
margin-right: 8px;
|
||||
margin-top: 3px; /* Align with text baseline */
|
||||
}
|
||||
.print-quantity {
|
||||
font-weight: bold;
|
||||
flex-shrink: 0;
|
||||
margin-right: 8px;
|
||||
}
|
||||
.print-name {
|
||||
flex-grow: 1;
|
||||
margin-right: 8px;
|
||||
word-break: break-word; /* Allow long names to wrap */
|
||||
}
|
||||
.print-details {
|
||||
color: #555;
|
||||
flex-shrink: 0;
|
||||
margin-right: 8px;
|
||||
white-space: nowrap; /* Keep details on one line for print-preview */
|
||||
}
|
||||
.print-total-weight {
|
||||
font-weight: bold;
|
||||
flex-shrink: 0;
|
||||
text-align: right;
|
||||
}
|
||||
.statistics-list {
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
}
|
||||
.statistics-list li {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
padding: 3px 0;
|
||||
border-bottom: 1px solid #eee;
|
||||
}
|
||||
.statistics-list li:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container-print">
|
||||
<?php if ($packing_list): ?>
|
||||
<h1><?php echo htmlspecialchars($packing_list['name']); ?></h1>
|
||||
<p class="description"><?php echo htmlspecialchars($packing_list['description'] ?: 'Keine Beschreibung'); ?></p>
|
||||
|
||||
<h2 class="section-title">Enthaltene Artikel</h2>
|
||||
<?php if (empty($packing_list_items)): ?>
|
||||
<p>Diese Packliste enthält noch keine Artikel.</p>
|
||||
<?php else: ?>
|
||||
<?php echo renderPrintablePackingListItemsRecursive($packing_list_items); ?>
|
||||
<?php endif; ?>
|
||||
|
||||
<h2 class="section-title">Statistiken</h2>
|
||||
<ul class="statistics-list">
|
||||
<li>
|
||||
<strong>Gesamtgewicht:</strong>
|
||||
<span><?php echo number_format($total_weight_grams, 0, ',', '.') . 'g'; ?></span>
|
||||
</li>
|
||||
<li>
|
||||
<strong>Basisgewicht:</strong>
|
||||
<span><?php echo number_format($total_weight_without_consumables, 0, ',', '.') . 'g'; ?></span>
|
||||
</li>
|
||||
<?php
|
||||
arsort($weight_by_category); // Sortiere Kategorien nach Gewicht absteigend
|
||||
foreach ($weight_by_category as $category_name => $weight) : ?>
|
||||
<li>
|
||||
<span>Gewicht <?php echo htmlspecialchars($category_name); ?>:</span>
|
||||
<span><?php echo number_format($weight, 0, ',', '.') . 'g'; ?></span>
|
||||
</li>
|
||||
<?php endforeach; ?>
|
||||
</ul>
|
||||
|
||||
<?php else: ?>
|
||||
<p style="text-align: center; color: red;"><?php echo htmlspecialchars($message); ?></p>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
<script>
|
||||
// Startet den Druckdialog, sobald die Seite geladen ist
|
||||
// setTimeout(function() { window.print(); }, 100); // Kleine Verzögerung zur Sicherheit
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
Before Width: | Height: | Size: 29 KiB After Width: | Height: | Size: 29 KiB |
|
Before Width: | Height: | Size: 768 KiB After Width: | Height: | Size: 768 KiB |
|
Before Width: | Height: | Size: 768 KiB After Width: | Height: | Size: 768 KiB |
|
Before Width: | Height: | Size: 768 KiB After Width: | Height: | Size: 768 KiB |
|
Before Width: | Height: | Size: 768 KiB After Width: | Height: | Size: 768 KiB |
|
Before Width: | Height: | Size: 580 KiB After Width: | Height: | Size: 580 KiB |
BIN
uploads/images/bp_img_69323e81d72cb8.63112841.jpg
Normal file
|
After Width: | Height: | Size: 1.1 MiB |
BIN
uploads/images/bp_img_6932403035e899.09967829.jpg
Normal file
|
After Width: | Height: | Size: 902 KiB |
BIN
uploads/images/bp_img_6932403756cb16.99209873.png
Normal file
|
After Width: | Height: | Size: 1.0 MiB |
0
src/uploads/images/img_686eade3358e94.97676456.jpg → uploads/images/bp_img_69324148109174.55311184.jpg
Executable file → Normal file
|
Before Width: | Height: | Size: 87 KiB After Width: | Height: | Size: 87 KiB |
|
Before Width: | Height: | Size: 236 KiB After Width: | Height: | Size: 236 KiB |
|
Before Width: | Height: | Size: 79 KiB After Width: | Height: | Size: 79 KiB |
|
Before Width: | Height: | Size: 768 KiB After Width: | Height: | Size: 768 KiB |
|
Before Width: | Height: | Size: 768 KiB After Width: | Height: | Size: 768 KiB |
|
Before Width: | Height: | Size: 80 KiB After Width: | Height: | Size: 80 KiB |
|
Before Width: | Height: | Size: 768 KiB After Width: | Height: | Size: 768 KiB |
|
Before Width: | Height: | Size: 768 KiB After Width: | Height: | Size: 768 KiB |
|
Before Width: | Height: | Size: 768 KiB After Width: | Height: | Size: 768 KiB |
|
Before Width: | Height: | Size: 768 KiB After Width: | Height: | Size: 768 KiB |
|
Before Width: | Height: | Size: 768 KiB After Width: | Height: | Size: 768 KiB |
|
Before Width: | Height: | Size: 768 KiB After Width: | Height: | Size: 768 KiB |
|
Before Width: | Height: | Size: 768 KiB After Width: | Height: | Size: 768 KiB |
|
Before Width: | Height: | Size: 768 KiB After Width: | Height: | Size: 768 KiB |
|
Before Width: | Height: | Size: 768 KiB After Width: | Height: | Size: 768 KiB |
|
Before Width: | Height: | Size: 768 KiB After Width: | Height: | Size: 768 KiB |
|
Before Width: | Height: | Size: 768 KiB After Width: | Height: | Size: 768 KiB |
|
Before Width: | Height: | Size: 768 KiB After Width: | Height: | Size: 768 KiB |
|
Before Width: | Height: | Size: 768 KiB After Width: | Height: | Size: 768 KiB |
|
Before Width: | Height: | Size: 768 KiB After Width: | Height: | Size: 768 KiB |
|
Before Width: | Height: | Size: 768 KiB After Width: | Height: | Size: 768 KiB |
|
Before Width: | Height: | Size: 768 KiB After Width: | Height: | Size: 768 KiB |
|
Before Width: | Height: | Size: 768 KiB After Width: | Height: | Size: 768 KiB |
|
Before Width: | Height: | Size: 768 KiB After Width: | Height: | Size: 768 KiB |
|
Before Width: | Height: | Size: 768 KiB After Width: | Height: | Size: 768 KiB |
|
Before Width: | Height: | Size: 236 KiB After Width: | Height: | Size: 236 KiB |
|
Before Width: | Height: | Size: 768 KiB After Width: | Height: | Size: 768 KiB |
|
Before Width: | Height: | Size: 768 KiB After Width: | Height: | Size: 768 KiB |
|
Before Width: | Height: | Size: 236 KiB After Width: | Height: | Size: 236 KiB |
|
Before Width: | Height: | Size: 668 KiB After Width: | Height: | Size: 668 KiB |
|
Before Width: | Height: | Size: 22 KiB After Width: | Height: | Size: 22 KiB |
BIN
uploads/images/img_686eade3358e94.97676456.jpg
Executable file
|
After Width: | Height: | Size: 87 KiB |
|
Before Width: | Height: | Size: 862 KiB After Width: | Height: | Size: 862 KiB |
|
Before Width: | Height: | Size: 422 KiB After Width: | Height: | Size: 422 KiB |
|
Before Width: | Height: | Size: 422 KiB After Width: | Height: | Size: 422 KiB |
|
Before Width: | Height: | Size: 46 KiB After Width: | Height: | Size: 46 KiB |
|
Before Width: | Height: | Size: 196 KiB After Width: | Height: | Size: 196 KiB |
|
Before Width: | Height: | Size: 218 KiB After Width: | Height: | Size: 218 KiB |
|
Before Width: | Height: | Size: 211 KiB After Width: | Height: | Size: 211 KiB |
|
Before Width: | Height: | Size: 188 KiB After Width: | Height: | Size: 188 KiB |
|
Before Width: | Height: | Size: 54 KiB After Width: | Height: | Size: 54 KiB |
|
Before Width: | Height: | Size: 61 KiB After Width: | Height: | Size: 61 KiB |
|
Before Width: | Height: | Size: 59 KiB After Width: | Height: | Size: 59 KiB |
|
Before Width: | Height: | Size: 71 KiB After Width: | Height: | Size: 71 KiB |
|
Before Width: | Height: | Size: 203 KiB After Width: | Height: | Size: 203 KiB |
|
Before Width: | Height: | Size: 98 KiB After Width: | Height: | Size: 98 KiB |
|
Before Width: | Height: | Size: 144 KiB After Width: | Height: | Size: 144 KiB |
|
Before Width: | Height: | Size: 77 KiB After Width: | Height: | Size: 77 KiB |
|
Before Width: | Height: | Size: 63 KiB After Width: | Height: | Size: 63 KiB |
|
Before Width: | Height: | Size: 61 KiB After Width: | Height: | Size: 61 KiB |
|
Before Width: | Height: | Size: 46 KiB After Width: | Height: | Size: 46 KiB |
|
Before Width: | Height: | Size: 81 KiB After Width: | Height: | Size: 81 KiB |
|
Before Width: | Height: | Size: 66 KiB After Width: | Height: | Size: 66 KiB |
|
Before Width: | Height: | Size: 118 KiB After Width: | Height: | Size: 118 KiB |